Skip to content

Sample Programs#

Chunks Sample#

/*
   Basler cameras provide "chunk features": The cameras can generate certain information about each image,
   e.g. frame counters, time stamps, and CRC checksums, which is appended to the image data as data "chunks".
   This sample illustrates how to enable chunk features, how to grab
   images, and how to process the appended data. When the camera is in chunk mode, it transfers data blocks
   that are partitioned into chunks. The first chunk is always the image data. When chunk features are enabled,
   the image data chunk is followed by chunks containing the information generated by the chunk features.

   This sample also demonstrates how to use software triggers. Two buffers are used. Once a buffer is filled,
   the acquisition of the next frame is triggered before processing the received buffer. This approach allows
   performing image acquisition while the processing of the previous image proceeds.
*/


#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>

#include <pylonc/PylonC.h>

#define CHECK( errc ) if ( GENAPI_E_OK != errc ) printErrorAndExit( errc )

/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void );

/* This method demonstrates how to retrieve the error message for the last failed function call. */
void printErrorAndExit( GENAPIC_RESULT errc );

/* Calculates the minimum and maximum gray value. */
void getMinMax( const unsigned char* pImg, int32_t width, int32_t height,
                unsigned char* pMin, unsigned char* pMax );

#define NUM_GRABS 20          /* Number of images to grab. */
#define NUM_BUFFERS 2         /* Number of buffers used for grabbing. */

int main( void )
{
    GENAPIC_RESULT              res;                        /* Return value of pylon methods. */
    size_t                      numDevices;                 /* Number of available devices. */
    PYLON_DEVICE_HANDLE         hDev;                       /* Handle for the pylon device. */
    PYLON_STREAMGRABBER_HANDLE  hGrabber;                   /* Handle for the pylon stream grabber. */
    PYLON_CHUNKPARSER_HANDLE    hChunkParser;               /* Handle for the parser extracting the chunk data. */
    PYLON_WAITOBJECT_HANDLE     hWait;                      /* Handle used for waiting for a grab to be finished. */
    size_t                      payloadSize;                /* Size of an image frame in bytes. */
    unsigned char*              buffers[NUM_BUFFERS];       /* Buffers used for grabbing. */
    PYLON_STREAMBUFFER_HANDLE   bufHandles[NUM_BUFFERS];    /* Handles for the buffers. */
    PylonGrabResult_t           grabResult;                 /* Stores the result of a grab operation. */
    int                         nGrabs;                     /* Counts the number of buffers grabbed. */
    size_t                      nStreams;                   /* The number of streams the device provides. */
    _Bool                       isAvail;                    /* Used for checking feature availability. */
    _Bool                       isReady;                    /* Used as an output parameter. */
    size_t                      i;                          /* Counter. */
    int                         ret = EXIT_FAILURE;         /* The return value. */
    const char*                 triggerSelectorValue = "FrameStart"; /* Preselect the trigger for image acquisition. */
    _Bool                       isAvailFrameStart;          /* Used for checking feature availability. */
    _Bool                       isAvailAcquisitionStart;    /* Used for checking feature availability. */

    hDev = PYLONC_INVALID_HANDLE;

    /* Before using any pylon methods, the pylon runtime must be initialized. */
    PylonInitialize();

    printf( "Enumerating devices ...\n" );

    /* Enumerate all camera devices. You must call
       PylonEnumerateDevices() before creating a device. */
    res = PylonEnumerateDevices( &numDevices );
    CHECK( res );
    if (0 == numDevices)
    {
        fprintf( stderr, "No devices found.\n" );
        /* Before exiting a program, PylonTerminate() should be called to release
           all pylon related resources. */
        PylonTerminate();
        pressEnterToExit();
        exit( EXIT_FAILURE );
    }

    printf( "Opening first device ...\n" );

    /* Get a handle for the first device found.  */
    res = PylonCreateDeviceByIndex( 0, &hDev );
    CHECK( res );

    /* Before using the device, it must be opened. Open it for setting
       parameters and for grabbing images. */
    res = PylonDeviceOpen( hDev, PYLONC_ACCESS_MODE_CONTROL | PYLONC_ACCESS_MODE_STREAM );
    CHECK( res );


    /* Set the pixel format to Mono8 if available, where gray values will be output as 8 bit values for each pixel. */
    isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_PixelFormat_Mono8" );
    if (isAvail)
    {
        res = PylonDeviceFeatureFromString( hDev, "PixelFormat", "Mono8" );
        CHECK( res );
    }

    /* Check the available camera trigger mode(s) to select the appropriate one: acquisition start trigger mode (used by previous cameras;
    do not confuse with acquisition start command) or frame start trigger mode (equivalent to previous acquisition start trigger mode). */
    isAvailAcquisitionStart = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_TriggerSelector_AcquisitionStart" );
    isAvailFrameStart = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_TriggerSelector_FrameStart" );

    /* Check to see if the camera implements the acquisition start trigger mode only. */
    if (isAvailAcquisitionStart && !isAvailFrameStart)
    {
    /* ... Select the software trigger as the trigger source. */
        res = PylonDeviceFeatureFromString( hDev, "TriggerSelector", "AcquisitionStart" );
        CHECK( res );
        res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "On" );
        CHECK( res );
        triggerSelectorValue = "AcquisitionStart";
    }
    else
    {
        /* Camera may have the acquisition start trigger mode and the frame start trigger mode implemented.
        In this case, the acquisition trigger mode must be switched off. */
        if (isAvailAcquisitionStart)
        {
            res = PylonDeviceFeatureFromString( hDev, "TriggerSelector", "AcquisitionStart" );
            CHECK( res );
            res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "Off" );
            CHECK( res );
        }

        /* Disable frame burst start trigger if available. */
        isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_TriggerSelector_FrameBurstStart" );
        if (isAvail)
        {
            res = PylonDeviceFeatureFromString( hDev, "TriggerSelector", "FrameBurstStart" );
            CHECK( res );
            res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "Off" );
            CHECK( res );
        }

        /* To trigger each single frame by software or external hardware trigger: Enable the frame start trigger mode. */
        res = PylonDeviceFeatureFromString( hDev, "TriggerSelector", "FrameStart" );
        CHECK( res );
        res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "On" );
        CHECK( res );
    }

    /* Note: the trigger selector must be set to the appropriate trigger mode
    before setting the trigger source or issuing software triggers.
    Frame start trigger mode for newer cameras, acquisition start trigger mode for previous cameras. */
    PylonDeviceFeatureFromString( hDev, "TriggerSelector", triggerSelectorValue );

    /* Enable software triggering. */
    /* ... Select the software trigger as the trigger source. */
    res = PylonDeviceFeatureFromString( hDev, "TriggerSource", "Software" );
    CHECK( res );

    /* When using software triggering, the Continuous frame mode should be used. Once
       acquisition is started, the camera sends one image each time a software trigger is
       issued. */
    res = PylonDeviceFeatureFromString( hDev, "AcquisitionMode", "Continuous" );
    CHECK( res );

    /* For GigE cameras, we recommend increasing the packet size for better
       performance. When the network adapter supports jumbo frames, set the packet
       size to a value > 1500, e.g., to 8192. In this sample, we only set the packet size
       to 1500. */
    /* ... Check first to see if the GigE camera packet size parameter is supported and if it is writable. */
    isAvail = PylonDeviceFeatureIsWritable( hDev, "GevSCPSPacketSize" );
    if (isAvail)
    {
        /* ... The device supports the packet size feature. Set a value. */
        res = PylonDeviceSetIntegerFeature( hDev, "GevSCPSPacketSize", 1500 );
        CHECK( res );
    }

    /* Before enabling individual chunks, the chunk mode in general must be activated. */
    isAvail = PylonDeviceFeatureIsWritable( hDev, "ChunkModeActive" );
    if (!isAvail)
    {
        fprintf( stderr, "The device doesn't support the chunk mode.\n" );
        PylonTerminate();
        pressEnterToExit();
        exit( EXIT_FAILURE );
    }

    /* Activate the chunk mode. */
    res = PylonDeviceSetBooleanFeature( hDev, "ChunkModeActive", 1 );
    CHECK( res );

    /* Enable some individual chunks... */

    /* ... The frame counter chunk feature. */
    /* Is the chunk available? */
    isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_ChunkSelector_Framecounter" );
    if (isAvail)
    {
        /* Select the frame counter chunk feature. */
        res = PylonDeviceFeatureFromString( hDev, "ChunkSelector", "Framecounter" );
        CHECK( res );
        /* Can the chunk feature be activated? */
        isAvail = PylonDeviceFeatureIsWritable( hDev, "ChunkEnable" );
        if (isAvail)
        {
            /* Activate the chunk feature. */
            res = PylonDeviceSetBooleanFeature( hDev, "ChunkEnable", 1 );
            CHECK( res );
        }
    }
    else
    {
        /* try setting Standard feature naming convention (SFNC) FrameID name*/
        isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_ChunkSelector_FrameID" );
        if (isAvail)
        {
            /* Select the frame id chunk feature. */
            res = PylonDeviceFeatureFromString( hDev, "ChunkSelector", "FrameID" );
            CHECK( res );
            /* Can the chunk feature be activated? */
            isAvail = PylonDeviceFeatureIsWritable( hDev, "ChunkEnable" );
            if (isAvail)
            {
                /* Activate the chunk feature. */
                res = PylonDeviceSetBooleanFeature( hDev, "ChunkEnable", 1 );
                CHECK( res );
            }
        }
    }
    /* ... The CRC checksum chunk feature. */
    /*  Note: Enabling the CRC chunk feature is not a prerequisite for using
        chunks. Chunks can also be handled when the CRC feature is disabled. */
    isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_ChunkSelector_PayloadCRC16" );
    if (isAvail)
    {
        /* Select the CRC chunk feature. */
        res = PylonDeviceFeatureFromString( hDev, "ChunkSelector", "PayloadCRC16" );
        CHECK( res );
        /* Can the chunk feature be activated? */
        isAvail = PylonDeviceFeatureIsWritable( hDev, "ChunkEnable" );
        if (isAvail)
        {
            /* Activate the chunk feature. */
            res = PylonDeviceSetBooleanFeature( hDev, "ChunkEnable", 1 );
            CHECK( res );
        }
    }
    /* ... The Timestamp chunk feature. */
    isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_ChunkSelector_Timestamp" );
    if (isAvail)
    {
        /* Select the Timestamp chunk feature. */
        res = PylonDeviceFeatureFromString( hDev, "ChunkSelector", "Timestamp" );
        CHECK( res );
        /* Can the chunk feature be activated? */
        isAvail = PylonDeviceFeatureIsWritable( hDev, "ChunkEnable" );
        if (isAvail)
        {
            /* Activate the chunk feature. */
            res = PylonDeviceSetBooleanFeature( hDev, "ChunkEnable", 1 );
            CHECK( res );
        }
    }

    /* The data block containing the image chunk and the other chunks has a self-descriptive layout.
       A chunk parser is used to extract the appended chunk data from the grabbed image frame.
       Create a chunk parser. */
    res = PylonDeviceCreateChunkParser( hDev, &hChunkParser );
    CHECK( res );
    if (hChunkParser == PYLONC_INVALID_HANDLE)
    {
        /* The transport layer doesn't provide a chunk parser. */
        fprintf( stderr, "No chunk parser available.\n" );
        goto exit;
    }


    /* Image grabbing is done using a stream grabber.
       A device may be able to provide different streams. A separate stream grabber must
       be used for each stream. In this sample, we create a stream grabber for the default
       stream, i.e., the first stream ( index == 0 ).
    */

    /* Get the number of streams supported by the device and the transport layer. */
    res = PylonDeviceGetNumStreamGrabberChannels( hDev, &nStreams );
    CHECK( res );
    if (nStreams < 1)
    {
        fprintf( stderr, "The transport layer doesn't support image streams.\n" );
        PylonTerminate();
        pressEnterToExit();
        exit( EXIT_FAILURE );
    }

    /* Create and open a stream grabber for the first channel. */
    res = PylonDeviceGetStreamGrabber( hDev, 0, &hGrabber );
    CHECK( res );
    res = PylonStreamGrabberOpen( hGrabber );
    CHECK( res );

    /* Get a handle for the stream grabber's wait object. The wait object
       allows waiting for buffers to be filled with grabbed data. */
    res = PylonStreamGrabberGetWaitObject( hGrabber, &hWait );
    CHECK( res );

    /* Determine the required size of the grab buffer. Since activating chunks will increase the
       payload size and thus the required buffer size, do this after enabling the chunks. */
    res = PylonStreamGrabberGetPayloadSize( hDev, hGrabber, &payloadSize );
    CHECK( res );

    /* Allocate memory for grabbing.  */
    for (i = 0; i < NUM_BUFFERS; ++i)
    {
        buffers[i] = (unsigned char*) malloc( payloadSize );
        if (NULL == buffers[i])
        {
            fprintf( stderr, "Out of memory.\n" );
            PylonTerminate();
            pressEnterToExit();
            exit( EXIT_FAILURE );
        }
    }

    /* We must tell the stream grabber the number and size of the buffers
       we are using. */
    /* .. We will not use more than NUM_BUFFERS for grabbing. */
    res = PylonStreamGrabberSetMaxNumBuffer( hGrabber, NUM_BUFFERS );
    CHECK( res );
    /* .. We will not use buffers bigger than payloadSize bytes. */
    res = PylonStreamGrabberSetMaxBufferSize( hGrabber, payloadSize );
    CHECK( res );


    /*  Allocate the resources required for grabbing. After this, critical parameters
        that impact the payload size must not be changed until FinishGrab() is called. */
    res = PylonStreamGrabberPrepareGrab( hGrabber );
    CHECK( res );


    /* Before using the buffers for grabbing, they must be registered at
    the stream grabber. For each registered buffer, a buffer handle
    is returned. After registering, these handles are used instead of the
    raw pointers. */
    for (i = 0; i < NUM_BUFFERS; ++i)
    {
        res = PylonStreamGrabberRegisterBuffer( hGrabber, buffers[i], payloadSize, &bufHandles[i] );
        CHECK( res );
    }

    /* Feed the buffers into the stream grabber's input queue. For each buffer, the API
       allows passing in a pointer to additional context information. This pointer
       will be returned unchanged when the grab is finished. In our example, we use the index of the
       buffer as context information. */
    for (i = 0; i < NUM_BUFFERS; ++i)
    {
        res = PylonStreamGrabberQueueBuffer( hGrabber, bufHandles[i], (void*) i );
        CHECK( res );
    }

    /* Start the image acquisition engine. */
    res = PylonStreamGrabberStartStreamingIfMandatory( hGrabber );
    CHECK( res );

    /* Issue an acquisition start command. Because the trigger mode is enabled, issuing the acquisition start command
       itself will not trigger any image acquisitions. Issuing the start command simply prepares the camera to acquire images.
       Once the camera is prepared it will acquire one image for every trigger it receives. */
    res = PylonDeviceExecuteCommandFeature( hDev, "AcquisitionStart" );
    CHECK( res );


    /* Trigger the first image. */
    res = PylonDeviceExecuteCommandFeature( hDev, "TriggerSoftware" );
    CHECK( res );


    /* Grab NUM_GRABS images */
    nGrabs = 0;                         /* Counts the number of images grabbed. */
    while (nGrabs < NUM_GRABS)
    {
        size_t bufferIndex;              /* Index of the buffer. */
        unsigned char min, max;
        int32_t chunkWidth = 0; /* Data retrieved from the chunk parser. */
        int32_t chunkHeight = 0; /* Data retrieved from the chunk parser. */

        /* Wait for the next buffer to be filled. Wait up to 1000 ms. */
        res = PylonWaitObjectWait( hWait, 1000, &isReady );
        CHECK( res );
        if (!isReady)
        {
            /* Timeout occurred. */
            fprintf( stderr, "Grab timeout occurred.\n" );
            break; /* Stop grabbing. */
        }

        /* Since the wait operation was successful, the result of at least one grab
           operation is available. Retrieve it. */
        res = PylonStreamGrabberRetrieveResult( hGrabber, &grabResult, &isReady );
        CHECK( res );
        if (!isReady)
        {
            /* Oops. No grab result available? We should never have reached this point.
               Since the wait operation above returned without a timeout, a grab result
               should be available. */
            fprintf( stderr, "Failed to retrieve a grab result\n" );
            break;
        }

        nGrabs++;

        /* Trigger the next image. Since we passed more than one buffer to the stream grabber,
           the triggered image will be grabbed while the image processing is performed.  */
        res = PylonDeviceExecuteCommandFeature( hDev, "TriggerSoftware" );
        CHECK( res );


        /* Get the buffer index from the context information. */
        bufferIndex = (size_t) grabResult.Context;

        /* Check to see if the image was grabbed successfully. */
        if (grabResult.Status == Grabbed)
        {
            /*  The grab is successfull.  */

            unsigned char* buffer;        /* Pointer to the buffer attached to the grab result. */

            /* Get the buffer pointer from the result structure. Since we also got the buffer index,
            we could alternatively use buffers[bufferIndex]. */
            buffer = (unsigned char*) grabResult.pBuffer;

            printf( "Grabbed frame #%2d into buffer %2d.\n", nGrabs, (int) bufferIndex );

            /* Check to see if we really got image data plus chunk data. */
            if (grabResult.PayloadType != PayloadType_ChunkData)
            {
                fprintf( stderr, "Received a buffer not containing chunk data?\n" );
            }
            else
            {
                /* Process the chunk data. This is done by passing the grabbed image buffer
                   to the chunk parser. When the chunk parser has processed the buffer, the chunk
                   data can be accessed in the same manner as "normal" camera parameters.
                   The only exception is the CRC feature. There are dedicated functions for
                   checking the CRC checksum. */

                _Bool hasCRC;

                /* Let the parser extract the data. */
                res = PylonChunkParserAttachBuffer( hChunkParser, grabResult.pBuffer, (size_t) grabResult.PayloadSize );
                CHECK( res );

                /* Check the CRC. */
                res = PylonChunkParserHasCRC( hChunkParser, &hasCRC );
                CHECK( res );
                if (hasCRC)
                {
                    _Bool isOk;
                    res = PylonChunkParserCheckCRC( hChunkParser, &isOk );
                    CHECK( res );
                    printf( "Frame %d contains a CRC checksum. The checksum %s ok.\n", nGrabs, isOk ? "is" : "is not" );
                }
                {
                    const char *featureName = "ChunkFramecounter";
                    /* Retrieve the frame counter value. */
                    /* ... Check the availability. */
                    isAvail = PylonDeviceFeatureIsAvailable( hDev, featureName );
                    if (!isAvail)
                    {
                        /*if not available try using the SFNC feature FrameID*/
                        featureName = "ChunkFrameID";
                        isAvail = PylonDeviceFeatureIsAvailable( hDev, featureName );
                    }
                    printf( "Frame %d %s a frame counter chunk.\n", nGrabs, isAvail ? "contains" : "doesn't contain" );
                    if (isAvail)
                    {
                        /* ... Get the value. */
                        int64_t counter;
                        res = PylonDeviceGetIntegerFeature( hDev, featureName, &counter );
                        CHECK( res );
#if __STDC_VERSION__ >= 199901L || defined(__GNUC__)
                        printf( "Frame counter of frame %d: %lld.\n", nGrabs, (long long) counter );
#else
                        printf( "Frame counter of frame %d: %I64d.\n", nGrabs, counter );
#endif
                    }
                }
                /* Retrieve the frame width value. */
                /* ... Check the availability. */
                isAvail = PylonDeviceFeatureIsAvailable( hDev, "ChunkWidth" );
                printf( "Frame %d %s a width chunk.\n", nGrabs, isAvail ? "contains" : "doesn't contain" );
                if (isAvail)
                {
                    /* ... Get the value. */
                    res = PylonDeviceGetIntegerFeatureInt32( hDev, "ChunkWidth", &chunkWidth );
                    CHECK( res );
                    printf( "Width of frame %d: %d.\n", nGrabs, chunkWidth );
                }

                /* Retrieve the frame height value. */
                /* ... Check the availability. */
                isAvail = PylonDeviceFeatureIsAvailable( hDev, "ChunkHeight" );
                printf( "Frame %d %s a height chunk.\n", nGrabs, isAvail ? "contains" : "doesn't contain" );
                if (isAvail)
                {
                    /* ... Get the value. */
                    res = PylonDeviceGetIntegerFeatureInt32( hDev, "ChunkHeight", &chunkHeight );
                    CHECK( res );
                    printf( "Height of frame %d: %d.\n", nGrabs, chunkHeight );
                }
                /* Retrieve the frame timestamp value. */
                /* ... Check the availability. */
                isAvail = PylonDeviceFeatureIsAvailable( hDev, "ChunkTimestamp" );
                printf( "Frame %d %s a timestamp chunk.\n", nGrabs, isAvail ? "contains" : "doesn't contain" );
                if (isAvail)
                {
                    /* ... Get the value. */
                    int64_t timestamp;
                    res = PylonDeviceGetIntegerFeature( hDev, "ChunkTimestamp", &timestamp );
                    CHECK( res );
#if __STDC_VERSION__ >= 199901L || defined(__GNUC__)
                    printf( "Frame timestamp of frame %d: %lld.\n", nGrabs, (long long)timestamp );
#else
                    printf( "Frame timestamp of frame %d: %I64d.\n", nGrabs, timestamp );
#endif
                }
            }

            /* Perform the image processing. */
            getMinMax( buffer, chunkWidth, chunkHeight, &min, &max );
            printf( "Min. gray value  = %3u, Max. gray value = %3u\n", min, max );

            /* Before requeueing the buffer, you should detach it from the chunk parser. */
            res = PylonChunkParserDetachBuffer( hChunkParser );  /* The chunk data in the buffer is now no longer accessible. */
            CHECK( res );


        }
        else if (grabResult.Status == Failed)
        {
            fprintf( stderr, "Frame %d wasn't grabbed successfully.  Error code = 0x%08X\n",
                     nGrabs, grabResult.ErrorCode );
        }

        /* Once finished with the processing, requeue the buffer to be filled again. */
        res = PylonStreamGrabberQueueBuffer( hGrabber, grabResult.hBuffer, (void*) bufferIndex );
        CHECK( res );

    }

    /* Clean up. */

    /*  ... Stop the camera. */
    res = PylonDeviceExecuteCommandFeature( hDev, "AcquisitionStop" );
    CHECK( res );

    /* ... Stop the image acquisition engine. */
    res = PylonStreamGrabberStopStreamingIfMandatory( hGrabber );
    CHECK( res );

    /* ... We must issue a flush call to ensure that all pending buffers are put into the
    stream grabber's output queue. */
    res = PylonStreamGrabberFlushBuffersToOutput( hGrabber );
    CHECK( res );

    /* ... The buffers can now be retrieved from the stream grabber. */
    do
    {
        res = PylonStreamGrabberRetrieveResult( hGrabber, &grabResult, &isReady );
        CHECK( res );
    } while (isReady);

    /* ... When all buffers are retrieved from the stream grabber, they can be deregistered.
       After deregistering the buffers, it is safe to free the memory. */

    for (i = 0; i < NUM_BUFFERS; ++i)
    {
        res = PylonStreamGrabberDeregisterBuffer( hGrabber, bufHandles[i] );
        CHECK( res );
        free( buffers[i] );
    }

    /* ... Release grabbing related resources. */
    res = PylonStreamGrabberFinishGrab( hGrabber );
    CHECK( res );

    /* After calling PylonStreamGrabberFinishGrab(), parameters that impact the payload size (e.g.,
       the AOI width and height parameters) are unlocked and can be modified again. */

    /* ... Close the stream grabber. */
    res = PylonStreamGrabberClose( hGrabber );
    CHECK( res );

    /* ... Release the chunk parser. */
    res = PylonDeviceDestroyChunkParser( hDev, hChunkParser );
    CHECK( res );

    ret = EXIT_SUCCESS;

exit:

    /*  Disable the software trigger and the chunk mode. */
    if (hDev != PYLONC_INVALID_HANDLE)
    {
        res = PylonDeviceSetBooleanFeature( hDev, "ChunkEnable", 0 );
        CHECK( res );
        res = PylonDeviceSetBooleanFeature( hDev, "ChunkModeActive", 0 );
        CHECK( res );
        res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "Off" );
        CHECK( res );
    }


    /* ... Close and release the pylon device. The stream grabber becomes invalid
       after closing the pylon device. Don't call stream grabber related methods after
       closing or releasing the device. */
    res = PylonDeviceClose( hDev );
    CHECK( res );
    res = PylonDestroyDevice( hDev );
    CHECK( res );


    /* ... Shut down the pylon runtime system. Don't call any pylon method after
       calling PylonTerminate(). */
    PylonTerminate();
    pressEnterToExit();
    return ret;
}

/* This function demonstrates how to retrieve the error message for the last failed
   function call. */
void printErrorAndExit( GENAPIC_RESULT errc )
{
    char* errMsg;
    size_t length;

    /* Retrieve the error message.
    ... First find out how big the buffer must be, */
    GenApiGetLastErrorMessage( NULL, &length );
    errMsg = (char*) malloc( length );
    /* ... and retrieve the message. */
    GenApiGetLastErrorMessage( errMsg, &length );

    fprintf( stderr, "%s (%#08x).\n", errMsg, (unsigned int) errc );
    free( errMsg );

    /* Retrieve the more details about the error.
    ... First find out how big the buffer must be, */
    GenApiGetLastErrorDetail( NULL, &length );
    errMsg = (char*) malloc( length );
    /* ... and retrieve the message. */
    GenApiGetLastErrorDetail( errMsg, &length );

    fprintf( stderr, "%s\n", errMsg );
    free( errMsg );

    PylonTerminate();  /* Releases all pylon resources. */
    pressEnterToExit();
    exit( EXIT_FAILURE );
}

/* Simple "image processing" function returning the minimum and maximum gray
   value of an image with 8 bit gray values. */
void getMinMax( const unsigned char* pImg, int32_t width, int32_t height,
                unsigned char* pMin, unsigned char* pMax )
{
    unsigned char min = 255;
    unsigned char max = 0;
    unsigned char val;
    const unsigned char* p;

    for (p = pImg; p < pImg + width * height; p++)
    {
        val = *p;
        if (val > max)
            max = val;
        if (val < min)
            min = val;
    }
    *pMin = min;
    *pMax = max;
}

/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void )
{
    fprintf( stderr, "\nPress enter to exit.\n" );
    while (getchar() != '\n');
}

ImageDecompressor Sample#

/*

This sample illustrates how to configure the Compression Beyond feature
on an ace 2 Pro camera.
This allows the camera to send compressed image data to the host computer.
The compressed image data can be decompressed on the host side using the
PylonImageDecompressor set of functions.

Using the image compression feature reduces the amount of data transferred,
which in turn can result in increasing the resulting frame rate.
When compression is used, the camera sends the compressed image data as
chunk data. You can use the PylonImageDecompressorDecompressImage function
to convert the the compressed chunk data into the pixel data.

Note: Not all camera models support image compression.
*/

#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>

#include <genapic/GenApiC.h>
#include <pylonc/PylonC.h>

/* Simple error handling. */
#define CHECK( errc ) if ( GENAPI_E_OK != errc ) printErrorAndExit( errc )

/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void );

/* This method demonstrates how to retrieve the error message
   for the last failed function call. */
void printErrorAndExit( GENAPIC_RESULT errc );

/* Calculating the minimum and maximum gray value of an image buffer. */
void getMinMax( const unsigned char* pImg, int32_t width, int32_t height,
                unsigned char* pMin, unsigned char* pMax );

/* Configure the camera to use compression. */
GENAPIC_RESULT configureCompression( PYLON_DEVICE_HANDLE hDev, double ratio );

int main( void )
{
    GENAPIC_RESULT          res;           /* Return value of pylon methods. */
    size_t                  numDevices;    /* Number of available devices. */
    PYLON_DEVICE_HANDLE     hDev;          /* Handle for the pylon device. */
    NODEMAP_HANDLE          hDeviceNodeMap;/* Handle for nodemap. */
    NODE_HANDLE             hDescNode;     /* Handle for the register node. */
    const int               numGrabs = 3;  /* Number of images to grab. */
    size_t                  payloadSize = 0; /* Size of the compressed image data in bytes. */
    size_t                  decompressedImageSize = 0; /* Size of a decompressed image frame in bytes. */
    size_t                  descriptorSize = 0; /* Size of the compression descriptor. */
    unsigned char*          descriptorBuf = NULL; /* Buffer used to store compression descriptor. */
    unsigned char*          imgBufCompressed = NULL; /* Buffer used to store compressed image data. */
    unsigned char*          imgBuf = NULL; /* Buffer used to store decompressed image data. */
    _Bool                   isAvail;
    int                     i;

    /* Before using any pylon methods, the pylon runtime must be initialized. */
    PylonInitialize();

    /* Enumerate all camera devices. You must call
    PylonEnumerateDevices() before creating a device! */
    res = PylonEnumerateDevices( &numDevices );
    CHECK( res );
    if (0 == numDevices)
    {
        fprintf( stderr, "No devices found!\n" );
        /* Before exiting a program, PylonTerminate() must be called to release
           all pylon-related resources. */
        PylonTerminate();
        pressEnterToExit();
        exit( EXIT_FAILURE );
    }

    /* Get a handle for the first device found.  */
    res = PylonCreateDeviceByIndex( 0, &hDev );
    CHECK( res );

    /* The device must be opened in order to configure parameters and grab images. */
    res = PylonDeviceOpen( hDev, PYLONC_ACCESS_MODE_CONTROL | PYLONC_ACCESS_MODE_STREAM );
    CHECK( res );

    /* Check whether the camera supports the compression feature. */
    isAvail = PylonDeviceFeatureIsAvailable( hDev, "ImageCompressionMode" );
    if (isAvail)
    {
        PYLON_IMAGE_DECOMPRESSOR_HANDLE hDecompressor = PYLONC_INVALID_HANDLE;
        double fps = 0.;

        /* Set the pixel format to Mono8, if available, where gray values will be output as 8-bit values for each pixel. */
        isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_PixelFormat_Mono8" );
        if (isAvail)
        {
            res = PylonDeviceFeatureFromString( hDev, "PixelFormat", "Mono8" );
            CHECK( res );
        }

        /* Disable acquisition start trigger, if available. */
        isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_TriggerSelector_AcquisitionStart" );
        if (isAvail)
        {
            res = PylonDeviceFeatureFromString( hDev, "TriggerSelector", "AcquisitionStart" );
            CHECK( res );
            res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "Off" );
            CHECK( res );
        }

        /* Disable frame burst start trigger, if available. */
        isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_TriggerSelector_FrameBurstStart" );
        if (isAvail)
        {
            res = PylonDeviceFeatureFromString( hDev, "TriggerSelector", "FrameBurstStart" );
            CHECK( res );
            res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "Off" );
            CHECK( res );
        }

        /* Disable frame start trigger, if available */
        isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_TriggerSelector_FrameStart" );
        if (isAvail)
        {
            res = PylonDeviceFeatureFromString( hDev, "TriggerSelector", "FrameStart" );
            CHECK( res );
            res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "Off" );
            CHECK( res );
        }

        /* For GigE cameras, Basler recommends increasing the packet size for better performance.
           If the network adapter supports jumbo frames,
           set the packet size to a value > 1500, e.g., to 8192.
           In this sample, we only set the packet size to 1500. */
        isAvail = PylonDeviceFeatureIsWritable( hDev, "GevSCPSPacketSize" );
        if (isAvail)
        {
            /* ... The device supports the packet size feature. Set a value. */
            res = PylonDeviceSetIntegerFeature( hDev, "GevSCPSPacketSize", 1500 );
            CHECK( res );
        }

        /* Turn off the compression feature to read the FPS without compression. */
        res = configureCompression( hDev, 0.0 );
        CHECK( res );

        res = PylonDeviceGetFloatFeature(hDev, "ResultingFrameRate", &fps);
        CHECK( res );
        printf( "Expected frames per second without compression: %.2f\n", fps );


        /* Configure the camera for lossless compression using a compression ratio of 75 %.
           You can adjust this value as required. */
        res = configureCompression( hDev, 75.0 );
        CHECK( res );

        /* Show the FPS with compression turned on. */
        res = PylonDeviceGetFloatFeature( hDev, "ResultingFrameRate", &fps );
        CHECK( res );
        printf( "Expected frames per second using compression: %.2f\n", fps );


        /* Determine the required size of the grab buffer. */
        {
            PYLON_STREAMGRABBER_HANDLE  hGrabber;
            int64_t value;

            /* Temporary create and open a stream grabber for the first channel. */
            res = PylonDeviceGetStreamGrabber( hDev, 0, &hGrabber );
            CHECK( res );
            res = PylonStreamGrabberOpen( hGrabber );
            CHECK( res );

            /* With compression turned on, the returned payload size is the 
               estimated maximum size of the compressed image data.
               The actual size of image data transferred may be smaller, depending on image contents. */
            res = PylonStreamGrabberGetPayloadSize( hDev, hGrabber, &payloadSize );
            CHECK( res );

            res = PylonStreamGrabberClose( hGrabber );
            CHECK( res );

            /* Get the size of the decompressed image data. */
            res = PylonDeviceGetIntegerFeature( hDev, "BslImageCompressionBCBDecompressedImageSize", &value );
            CHECK( res );
            decompressedImageSize = (size_t) value;
        }

        /* Create the decompressor. */
        res = PylonImageDecompressorCreate( &hDecompressor );
        CHECK( res );

        /* Get size of the compression descriptor required to decompress the image data from a register node.
           There is no pylon convenience function to read register nodes, so we use genapi functions. */
        res = PylonDeviceGetNodeMap( hDev, &hDeviceNodeMap );
        CHECK( res );
        res = GenApiNodeMapGetNode( hDeviceNodeMap, "BslImageCompressionBCBDescriptor", &hDescNode );
        CHECK( res );

        /* Get the length of the register node. */
        res = GenApiRegisterGetLength( hDescNode, &descriptorSize );
        CHECK( res );

        /* Allocate memory for the descriptor.
           The descriptor is small, allocation should not fail. */
        descriptorBuf = (unsigned char*) malloc( descriptorSize );

        /* Read the compression descriptor from the camera. */
        res = GenApiRegisterGetValue( hDescNode, descriptorBuf, &descriptorSize );
        CHECK( res );

        /* Set the descriptor in the decompressor. */
        res = PylonImageDecompressorSetCompressionDescriptor( hDecompressor, descriptorBuf, descriptorSize );
        CHECK( res );

        /* The decompresser has stored a copy of the descriptor. We can now free the descriptor. */
        free( descriptorBuf );
        descriptorBuf = NULL;

        /* Allocate memory. */
        imgBufCompressed = (unsigned char*) malloc( payloadSize );
        imgBuf = (unsigned char*) malloc( decompressedImageSize );

        if (NULL == imgBufCompressed || NULL == imgBuf)
        {
            fprintf( stderr, "Out of memory.\n" );
            PylonTerminate();
            pressEnterToExit();
            exit( EXIT_FAILURE );
        }

        /* Grab some images in a loop. */
        for (i = 0; i < numGrabs; ++i)
        {
            PylonGrabResult_t grabResult = { 0 };
            PylonCompressionInfo_t compInfo = { 0 };
            _Bool bufferReady;

            /* Grab a single frame from stream channel 0.
               The camera is set to single frame acquisition mode.
               Wait up to 2000 ms for the image to be grabbed. */
            res = PylonDeviceGrabSingleFrame( hDev, 0, imgBufCompressed, payloadSize,
                                              &grabResult, &bufferReady, 2000 );
            if (GENAPI_E_OK == res && !bufferReady)
            {
                /* Timeout occurred. */
                printf( "Frame %d: timeout\n", i + 1 );
            }
            CHECK( res );

            /* Check to see if the data has been received successfully. */
            if (grabResult.Status == Grabbed && grabResult.PayloadType == PayloadType_ChunkData)
            {
                /* Retrieve infos about the compressed image data (sent as chunk data). */
                res = PylonImageDecompressorGetCompressionInfo( grabResult.pBuffer, (size_t)grabResult.PayloadSize, &compInfo );
                CHECK( res );

                /* Does the returned chunk payload contain a successfully compressed image? */
                if (compInfo.HasCompressedImage && compInfo.CompressionStatus == CompressionStatus_Ok)
                {
                    unsigned char min, max;
                    double ratio = 0;

                    /* Decompress the chunk data into imgBuf. */
                    res = PylonImageDecompressorDecompressImage( hDecompressor, imgBuf, &decompressedImageSize, grabResult.pBuffer, (size_t) grabResult.PayloadSize, NULL );
                    CHECK( res );

                    /* Use the actual size of the returned data. */
                    ratio = (double)grabResult.PayloadSize / (double) compInfo.DecompressedPayloadSize;

                    /* Compressed images are sent as chunk data (PayloadType_ChunkData).
                       Most members of grabResult don't contain valid data.
                       This information can be retrieved from compInfo. */

                    /* Success. Now you can perform image processing on the image. */
                    getMinMax( imgBuf, compInfo.SizeX, compInfo.SizeY, &min, &max );
                    printf( "Grabbed frame #%2d: Compression Ratio: %.2f%%, Min. gray value = %3u, Max. gray value = %3u\n", i + 1, ratio, min, max );

#ifdef GENAPIC_WIN_BUILD
                    /* Display image. */
                    res = PylonImageWindowDisplayImage( 0, imgBuf, decompressedImageSize, compInfo.PixelType, compInfo.SizeX, compInfo.SizeY, compInfo.PaddingX, ImageOrientation_TopDown );
                    CHECK( res );
#endif
                }
                else
                {
                    printf( "Grabbed frame #%2d: Camera could not compress image. CompressionStatus = %d\n", i + 1, compInfo.CompressionStatus );
                }
            }
            else if (grabResult.Status == Failed)
            {
                fprintf( stderr, "Frame %d wasn't grabbed successfully.  Error code = 0x%08X\n",
                         i + 1, grabResult.ErrorCode );
            }
        }

        /* Free memory. */
        free( imgBuf );
        free( imgBufCompressed );

        /* Free all resources allocated by the decompressor. */
        res = PylonImageDecompressorDestroy( hDecompressor );
        CHECK( res );

        /* Turn off compression. */
        res = configureCompression( hDev, 0.0 );
        CHECK( res );
    }
    else
    {
        printf( "Camera does not support compression.\n");
    }

    /* Clean up. Close and release the pylon device. */

    res = PylonDeviceClose( hDev );
    CHECK( res );
    res = PylonDestroyDevice( hDev );
    CHECK( res );

    pressEnterToExit();

    /* Shut down the pylon runtime system. Don't call any pylon method after
       calling PylonTerminate(). */
    PylonTerminate();

    return EXIT_SUCCESS;
}

/* This function demonstrates how to retrieve the error message for the last failed
   function call. */
void printErrorAndExit( GENAPIC_RESULT errc )
{
    char* errMsg;
    size_t length;

    /* Retrieve the error message.
    ... Find out first how big the buffer must be, */
    GenApiGetLastErrorMessage( NULL, &length );
    errMsg = (char*) malloc( length );
    /* ... and retrieve the message. */
    GenApiGetLastErrorMessage( errMsg, &length );

    fprintf( stderr, "%s (%#08x).\n", errMsg, (unsigned int) errc );
    free( errMsg );

    /* Retrieve more details about the error.
    ... Find out first how big the buffer must be, */
    GenApiGetLastErrorDetail( NULL, &length );
    errMsg = (char*) malloc( length );
    /* ... and retrieve the message. */
    GenApiGetLastErrorDetail( errMsg, &length );

    fprintf( stderr, "%s\n", errMsg );
    free( errMsg );

    PylonTerminate();  /* Releases all pylon resources. */
    pressEnterToExit();

    exit( EXIT_FAILURE );
}



/* Simple "image processing" function returning the minimum and maximum gray
   value of an 8-bit gray value image. */
void getMinMax( const unsigned char* pImg, int32_t width, int32_t height,
                unsigned char* pMin, unsigned char* pMax )
{
    unsigned char min = 255;
    unsigned char max = 0;
    unsigned char val;
    const unsigned char* p;

    for (p = pImg; p < pImg + width * height; p++)
    {
        val = *p;
        if (val > max)
            max = val;
        if (val < min)
            min = val;
    }
    *pMin = min;
    *pMax = max;
}

/* This function configures the camera to use compression.
   The ratio parameter will set the BslImageCompressionRatio feature on the camera.
   If you pass a ratio of 0 compression will be turned off. */
GENAPIC_RESULT configureCompression( PYLON_DEVICE_HANDLE hDev, double ratio )
{
    GENAPIC_RESULT res = GENAPI_E_OK;

    if (ratio == 0)
    {
        /* Turn compression off. */
        res = PylonDeviceFeatureFromString( hDev, "ImageCompressionMode", "Off" );
    }
    else
    {
        /* Turn the compression feature on. */
        res = PylonDeviceFeatureFromString( hDev, "ImageCompressionMode", "BaslerCompressionBeyond" );
        if (res != GENAPI_E_OK) return res;

        /* We're using lossless compression, so image quality is not affected. */
        res = PylonDeviceFeatureFromString( hDev, "ImageCompressionRateOption", "Lossless" );
        if (res != GENAPI_E_OK) return res;

        /* In this sample we use a compression ratio of 75 %.
           You can adjust this value depending on your image contents and the required frame-rate.
           In addition, you can also configure the camera for lossy compression. This is not demonstrated in this sample.
           For more information, refer to the Basler Product Documentation.*/
        res = PylonDeviceSetFloatFeature( hDev, "BslImageCompressionRatio", ratio );
        if (res != GENAPI_E_OK) return res;
    }

    return res;
}

/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void )
{
    fprintf( stderr, "\nPress enter to exit.\n" );
    while (getchar() != '\n');
}

Events Sample#

/*
Basler GigE Vision cameras can send event messages. For example, when a sensor
exposure has finished, the camera can send an end-of-exposure event to the PC. The event
can be received by the PC before the image data for the finished exposure has been completely
transferred. This sample illustrates the retrieving and processing of event messages.

Receiving events is very similar to grabbing images. An event grabber provides a wait object that
is signalled when an event message is available. When an event message is available, it can be
retrieved from the event grabber. In contrast to grabbing images, memory buffers for receiving
events need not be provided by the application. Memory buffers to store event messages are organized
by the event grabber itself.

The specific layout of event messages depends on the event type and the camera type. The pylon API
uses GenICam support for parsing event messages. This means that the message layout is described in the
camera's XML description file. A GenApi node map is created from the XML camera description file.
This node map contains node objects representing the elements of the XML file. Since the layout of event
messages is described in the camera description file, the information carried by the event messages is
exposed as nodes in the node map and can be accessed like "normal" camera parameters.


You can register callback functions that are fired when a parameter has been changed. To be
informed that a received event message contains a specific event, a callback must be registered for
the parameter(s) associated with the event.

These mechanisms are demonstrated with the end-of-exposure event. The event carries the following
information:
* ExposureEndEventFrameID: indicates the number of the image frame that has been exposed.
* ExposureEndEventTimestamp: indicates the moment when the event has been generated.
* ExposureEndEventStreamChannelIndex: indicates the number of the image data stream used to
transfer the exposed frame.
A callback for the ExposureEndEventFrameID will be registered as an indicator for the arrival
of an end-of-exposure event.

*/


#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>

#include <pylonc/PylonC.h>

#define CHECK( errc ) if ( GENAPI_E_OK != errc ) printErrorAndExit( errc )

/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void );

/* This method demonstrates how to retrieve the error message for the last failed function call. */
void printErrorAndExit( GENAPIC_RESULT errc );

/* Calculates the minimum and maximum gray value. */
void getMinMax( const unsigned char* pImg, int32_t width, int32_t height,
                unsigned char* pMin, unsigned char* pMax );

 /* The function to be fired when an end of exposure event has been received. */
void GENAPIC_CC endOfExposureCallback( NODE_HANDLE hNode );


#define NUM_GRABS 100          /* Number of images to grab. */
#define NUM_IMAGE_BUFFERS 5   /* Number of buffers used for grabbing. */
#define NUM_EVENT_BUFFERS 20  /* Number of buffers used for grabbing. */

int main( void )
{
    GENAPIC_RESULT              res;                      /* Return value of pylon methods. */
    size_t                      numDevices;               /* Number of available devices. */
    PYLON_DEVICE_HANDLE         hDev;                     /* Handle for the pylon device. */
    PYLON_STREAMGRABBER_HANDLE  hStreamGrabber;           /* Handle for the pylon stream grabber. */
    PYLON_EVENTGRABBER_HANDLE   hEventGrabber;            /* Handle for the event grabber used for receiving events. */
    PYLON_EVENTADAPTER_HANDLE   hEventAdapter;            /* Handle for the event adapter used for dispatching events. */
    PYLON_WAITOBJECT_HANDLE     hWaitStream;              /* Handle used for waiting for a grab to be finished. */
    PYLON_WAITOBJECT_HANDLE     hWaitEvent;               /* Handle used for waiting for an event message. */
    PYLON_WAITOBJECTS_HANDLE    hWaitObjects;             /* Container allowing waiting for multiple wait objects. */
    NODEMAP_HANDLE              hNodeMap;                 /* Handle for the node map containing the
                                                             camera parameters. */
    NODE_CALLBACK_HANDLE        hCallback;                /* Used for deregistering a callback function. */
    NODE_HANDLE                 hNode;                    /* Handle for a camera parameter. */
    size_t                      payloadSize;              /* Size of an image in bytes. */
    unsigned char*              buffers[NUM_IMAGE_BUFFERS]; /* Buffers used for grabbing. */
    PYLON_STREAMBUFFER_HANDLE   bufHandles[NUM_IMAGE_BUFFERS]; /* Handles for the buffers. */
    PylonGrabResult_t           grabResult;               /* Stores the result of a grab operation. */
    int                         nGrabs;                   /* Counts the number of buffers grabbed. */
    size_t                      nStreams;                 /* The number of streams the device provides. */
    _Bool                       isAvail;                  /* Used for checking feature availability. */
    _Bool                       isReady;                  /* Used as an output parameter. */
    size_t                      i;                        /* Counter. */
    int32_t                     sfncVersionMajor;         /* The major number of the Standard Feature Naming Convention (SFNC)
                                                             version used by the camera device. */

    /* Before using any pylon methods, the pylon runtime must be initialized. */
    PylonInitialize();

    /* Enumerate all camera devices. You must call
    PylonEnumerateDevices() before creating a device. */
    res = PylonEnumerateDevices( &numDevices );
    CHECK( res );
    if (0 == numDevices)
    {
        fprintf( stderr, "No devices found.\n" );
        PylonTerminate();
        pressEnterToExit();
        exit( EXIT_FAILURE );
    }

    /* Get a handle for the first device found.  */
    res = PylonCreateDeviceByIndex( 0, &hDev );
    CHECK( res );

    /* Before using the device, it must be opened. Open it for settig
       parameters, for grabbing images, and for grabbing events. */
    res = PylonDeviceOpen( hDev, PYLONC_ACCESS_MODE_CONTROL | PYLONC_ACCESS_MODE_STREAM | PYLONC_ACCESS_MODE_EVENT );
    CHECK( res );

    /* Print out the name of the camera we are using. */
    {
        char buf[256];
        size_t siz = sizeof( buf );
        _Bool isReadable;

        isReadable = PylonDeviceFeatureIsReadable( hDev, "DeviceModelName" );
        if (isReadable)
        {
            res = PylonDeviceFeatureToString( hDev, "DeviceModelName", buf, &siz );
            CHECK( res );
            printf( "Using camera %s\n", buf );
        }
    }

    /* Set the pixel format to Mono8 if available, where gray values will be output as 8 bit values for each pixel. */
    isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_PixelFormat_Mono8" );
    if (isAvail)
    {
        res = PylonDeviceFeatureFromString( hDev, "PixelFormat", "Mono8" );
        CHECK( res );
    }

    /* Disable acquisition start trigger if available */
    isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_TriggerSelector_AcquisitionStart" );
    if (isAvail)
    {
        res = PylonDeviceFeatureFromString( hDev, "TriggerSelector", "AcquisitionStart" );
        CHECK( res );
        res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "Off" );
        CHECK( res );
    }

    /* Disable frame burst start trigger if available. */
    isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_TriggerSelector_FrameBurstStart" );
    if (isAvail)
    {
        res = PylonDeviceFeatureFromString( hDev, "TriggerSelector", "FrameBurstStart" );
        CHECK( res );
        res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "Off" );
        CHECK( res );
    }

    /* Disable frame start trigger if available */
    isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_TriggerSelector_FrameStart" );
    if (isAvail)
    {
        res = PylonDeviceFeatureFromString( hDev, "TriggerSelector", "FrameStart" );
        CHECK( res );
        res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "Off" );
        CHECK( res );
    }

    /* We will use the Continuous frame mode, i.e., the camera delivers
    images continuously. */
    res = PylonDeviceFeatureFromString( hDev, "AcquisitionMode", "Continuous" );
    CHECK( res );


    /* For GigE cameras, we recommend increasing the packet size for better
       performance. If the network adapter supports jumbo frames, set the packet
       size to a value > 1500, e.g., to 8192. In this sample, we only set the packet size
       to 1500. */
    /* ... Check first to see if the GigE camera packet size parameter is supported and if it is writable. */
    isAvail = PylonDeviceFeatureIsWritable( hDev, "GevSCPSPacketSize" );
    if (isAvail)
    {
        /* ... The device supports the packet size feature, set a value. */
        res = PylonDeviceSetIntegerFeature( hDev, "GevSCPSPacketSize", 1500 );
        CHECK( res );
    }

    isAvail = PylonDeviceFeatureIsWritable( hDev, "EventSelector" );
    if (!isAvail)
    {
        /* Feature is not available. */
        fprintf( stderr, "Device doesn't support events.\n" );
        PylonTerminate();
        pressEnterToExit();
        exit( EXIT_FAILURE );
    }

    /* Determine the major number of the SFNC version used by the camera device. */
    if (PylonDeviceGetIntegerFeatureInt32( hDev, "DeviceSFNCVersionMajor", &sfncVersionMajor ) != GENAPI_E_OK)
    {
        /* No SFNC version information is provided by the camera device. */
        sfncVersionMajor = 0;
    }

    /* Enable camera event reporting. */
    /* Select the end-of-exposure event reporting. */
    res = PylonDeviceFeatureFromString( hDev, "EventSelector", "ExposureEnd" );
    CHECK( res );
    /* Enable the event reporting.
    Select the enumeration entry name depending on the SFNC version used by the camera device.
    */
    if (sfncVersionMajor >= 2)
        res = PylonDeviceFeatureFromString( hDev, "EventNotification", "On" );
    else
        res = PylonDeviceFeatureFromString( hDev, "EventNotification", "GenICamEvent" );
    CHECK( res );

    /* Image grabbing is done using a stream grabber.
    A device may be able to provide different streams. A separate stream grabber must
    be used for each stream. In this sample, we create a stream grabber for the default
    stream, i.e., the first stream ( index == 0 ).
    */

    /* Get the number of streams supported by the device and the transport layer. */
    res = PylonDeviceGetNumStreamGrabberChannels( hDev, &nStreams );
    CHECK( res );
    if (nStreams < 1)
    {
        fprintf( stderr, "The transport layer doesn't support image streams\n" );
        PylonTerminate();
        pressEnterToExit();
        exit( EXIT_FAILURE );
    }

    /* Create and open a stream grabber for the first channel. */
    res = PylonDeviceGetStreamGrabber( hDev, 0, &hStreamGrabber );
    CHECK( res );
    res = PylonStreamGrabberOpen( hStreamGrabber );
    CHECK( res );

    /* Get a handle for the stream grabber's wait object. The wait object
    allows waiting for buffers to be grabbed. */
    res = PylonStreamGrabberGetWaitObject( hStreamGrabber, &hWaitStream );
    CHECK( res );

    res = PylonStreamGrabberGetPayloadSize( hDev, hStreamGrabber, &payloadSize );
    CHECK( res );

    /* Allocate memory for grabbing.  */
    for (i = 0; i < NUM_IMAGE_BUFFERS; ++i)
    {
        buffers[i] = (unsigned char*) malloc( payloadSize );
        if (NULL == buffers[i])
        {
            fprintf( stderr, "Out of memory!\n" );
            PylonTerminate();
            pressEnterToExit();
            exit( EXIT_FAILURE );
        }
    }

    /* We must tell the stream grabber the number and size of the buffers
    we are using. */
    /* .. We will not use more than NUM_BUFFERS for grabbing. */
    res = PylonStreamGrabberSetMaxNumBuffer( hStreamGrabber, NUM_IMAGE_BUFFERS );
    CHECK( res );
    /* .. We will not use buffers bigger than payloadSize bytes. */
    res = PylonStreamGrabberSetMaxBufferSize( hStreamGrabber, payloadSize );
    CHECK( res );


    /*  Allocate the resources required for grabbing. After this, critical parameters
    that impact the payload size must not be changed until FinishGrab() is called. */
    res = PylonStreamGrabberPrepareGrab( hStreamGrabber );
    CHECK( res );


    /* Before using the buffers for grabbing, they must be registered at
    the stream grabber. For each registered buffer, a buffer handle
    is returned. After registering, these handles are used instead of the
    raw pointers. */
    for (i = 0; i < NUM_IMAGE_BUFFERS; ++i)
    {
        res = PylonStreamGrabberRegisterBuffer( hStreamGrabber, buffers[i], payloadSize, &bufHandles[i] );
        CHECK( res );
    }

    /* Feed the buffers into the stream grabber's input queue. For each buffer, the API
    allows passing in a pointer to additional context information. This pointer
    will be returned unchanged when the grab is finished. In our example, we use the index of the
    buffer as context information. */
    for (i = 0; i < NUM_IMAGE_BUFFERS; ++i)
    {
        res = PylonStreamGrabberQueueBuffer( hStreamGrabber, bufHandles[i], (void*) i );
        CHECK( res );
    }

    /* The stream grabber is now prepared. As soon as the camera starts to acquire images,
    the image data will be grabbed into the provided buffers.  */


    /* Create and prepare an event grabber. */
    /* ... Get a handle for the event grabber. */
    res = PylonDeviceGetEventGrabber( hDev, &hEventGrabber );
    CHECK( res );
    if (hEventGrabber == PYLONC_INVALID_HANDLE)
    {
        /* The transport layer doesn't support event grabbers. */
        fprintf( stderr, "No event grabber supported.\n" );
        PylonTerminate();
        pressEnterToExit();
        return EXIT_FAILURE;
    }

    /* ... Tell the grabber how many buffers to use. */
    res = PylonEventGrabberSetNumBuffers( hEventGrabber, NUM_EVENT_BUFFERS );
    CHECK( res );

    /* ... Open the event grabber. */
    res = PylonEventGrabberOpen( hEventGrabber );  /* The event grabber is now ready
                                                   for receiving events. */
    CHECK( res );

    /* Retrieve the wait object that is associated with the event grabber. The event
    will be signaled when an event message has been received. */
    res = PylonEventGrabberGetWaitObject( hEventGrabber, &hWaitEvent );
    CHECK( res );

    /* For extracting the event data from an event message, an event adapter is used. */
    res = PylonDeviceCreateEventAdapter( hDev, &hEventAdapter );
    CHECK( res );
    if (hEventAdapter == PYLONC_INVALID_HANDLE)
    {
        /* The transport layer doesn't support event grabbers. */
        fprintf( stderr, "No event adapter supported.\n" );
        PylonTerminate();
        pressEnterToExit();
        return EXIT_FAILURE;
    }

    /* Register the callback function for ExposureEndEventFrameID parameter. */
    /*... Get the node map containing all parameters. */
    res = PylonDeviceGetNodeMap( hDev, &hNodeMap );
    CHECK( res );
    /* Get the ExposureEndEventFrameID parameter.
    Select the parameter name depending on the SFNC version used by the camera device.
    */
    if (sfncVersionMajor >= 2)
        res = GenApiNodeMapGetNode( hNodeMap, "EventExposureEndFrameID", &hNode );
    else
        res = GenApiNodeMapGetNode( hNodeMap, "ExposureEndEventFrameID", &hNode );
    CHECK( res );

    if (GENAPIC_INVALID_HANDLE == hNode)
    {
        /* There is no ExposureEndEventFrameID parameter. */
        fprintf( stderr, "There is no ExposureEndEventFrameID or EventExposureEndFrameID parameter.\n" );
        PylonTerminate();
        pressEnterToExit();
        return EXIT_FAILURE;
    }

    /* ... Register the callback function. */
    res = GenApiNodeRegisterCallback( hNode, endOfExposureCallback, &hCallback );
    CHECK( res );

    /* Put the wait objects into a container. */
    /* ... Create the container. */
    res = PylonWaitObjectsCreate( &hWaitObjects );
    CHECK( res );
    /* ... Add the wait objects' handles. */
    res = PylonWaitObjectsAddMany( hWaitObjects, 2, hWaitEvent, hWaitStream );
    CHECK( res );

    /* Start the image acquisition engine. */
    res = PylonStreamGrabberStartStreamingIfMandatory( hStreamGrabber );
    CHECK( res );

    /* Let the camera acquire images. */
    res = PylonDeviceExecuteCommandFeature( hDev, "AcquisitionStart" );
    CHECK( res );

    /* Grab NUM_GRABS images. */
    nGrabs = 0;                         /* Counts the number of images grabbed. */
    while (nGrabs < NUM_GRABS)
    {
        size_t bufferIndex;              /* Index of the buffer. */
        size_t waitObjectIndex;          /* Index of the wait object that is signalled.*/
        unsigned char min, max;

        /* Wait for either an image buffer grabbed or an event received. Wait up to 1000 ms. */
        res = PylonWaitObjectsWaitForAny( hWaitObjects, 1000, &waitObjectIndex, &isReady );
        CHECK( res );
        if (!isReady)
        {
            /* Timeout occurred. */
            fprintf( stderr, "Timeout. Neither grabbed an image nor received an event.\n" );
            break; /* Stop grabbing. */
        }

        if (0 == waitObjectIndex)
        {
            PylonEventResult_t eventMsg;
            /* hWaitEvent has been signalled. At least one event message is available. Retrieve it. */
            res = PylonEventGrabberRetrieveEvent( hEventGrabber, &eventMsg, &isReady );
            CHECK( res );
            if (!isReady)
            {
                /* Oops. No event message available? We should never have reached this point.
                Since the wait operation above returned without a timeout, an event message
                should be available. */
                fprintf( stderr, "Failed to retrieve an event\n" );
                break;
            }
            /* Check to see if the event was successfully received. */
            if (0 == eventMsg.ErrorCode)
            {
                /* Successfully received an event message. */
                /* Pass the event message to the event adapter. The event adapter will
                update the parameters related to events and will fire the callbacks
                registered to event related parameters. */
                res = PylonEventAdapterDeliverMessage( hEventAdapter, &eventMsg );
                CHECK( res );
            }
            else
            {
                fprintf( stderr, "Error when receiving an event: 0x%08x\n", eventMsg.ErrorCode );
            }
        }
        else if (1 == waitObjectIndex)
        {
            /* hWaitStream has been signalled. The result of at least one grab
            operation is available. Retrieve it. */
            res = PylonStreamGrabberRetrieveResult( hStreamGrabber, &grabResult, &isReady );
            CHECK( res );
            if (!isReady)
            {
                /* Oops. No grab result available? We should never have reached this point.
                Since the wait operation above returned without a timeout, a grab result
                should be available. */
                fprintf( stderr, "Failed to retrieve a grab result\n" );
                break;
            }

            nGrabs++;

            /* Get the buffer index from the context information. */
            bufferIndex = (size_t) grabResult.Context;

            /* Check to see if the image was grabbed successfully. */
            if (grabResult.Status == Grabbed)
            {
                /*  Success. Perform image processing. Since we passed more than one buffer
                to the stream grabber, the remaining buffers are filled while
                we do the image processing. The processed buffer won't be touched by
                the stream grabber until we pass it back to the stream grabber. */

                unsigned char* buffer;        /* Pointer to the buffer attached to the grab result. */

                /* Get the buffer pointer from the result structure. Since we also got the buffer index,
                we could alternatively use buffers[bufferIndex]. */
                buffer = (unsigned char*) grabResult.pBuffer;


                getMinMax( buffer, grabResult.SizeX, grabResult.SizeY, &min, &max );
                printf( "Grabbed frame #%2d into buffer %2d. Min. gray value = %3u, Max. gray value = %3u\n",
                        nGrabs, (int) bufferIndex, min, max );
            }
            else if (grabResult.Status == Failed)
            {
                fprintf( stderr, "Frame %d wasn't grabbed successfully.  Error code = 0x%08X\n",
                         nGrabs, grabResult.ErrorCode );
            }

            /* Once finished with the processing, requeue the buffer to be filled again. */
            res = PylonStreamGrabberQueueBuffer( hStreamGrabber, grabResult.hBuffer, (void*) bufferIndex );
            CHECK( res );
        }
    }

    /* Clean up. */

    /*  ... Stop the camera. */
    res = PylonDeviceExecuteCommandFeature( hDev, "AcquisitionStop" );
    CHECK( res );

    /* ... Stop the image acquisition engine. */
    res = PylonStreamGrabberStopStreamingIfMandatory( hStreamGrabber );
    CHECK( res );

    /* ... Switch-off the events. */
    res = PylonDeviceFeatureFromString( hDev, "EventSelector", "ExposureEnd" );
    CHECK( res );
    res = PylonDeviceFeatureFromString( hDev, "EventNotification", "Off" );
    CHECK( res );


    /* ... We must issue a flush call to ensure that all pending buffers are put into the
    stream grabber's output queue. */
    res = PylonStreamGrabberFlushBuffersToOutput( hStreamGrabber );
    CHECK( res );

    /* ... The buffers can now be retrieved from the stream grabber. */
    do
    {
        res = PylonStreamGrabberRetrieveResult( hStreamGrabber, &grabResult, &isReady );
        CHECK( res );
    } while (isReady);

    /* ... When all buffers are retrieved from the stream grabber, they can be deregistered.
    After deregistering the buffers, it is safe to free the memory. */

    for (i = 0; i < NUM_IMAGE_BUFFERS; ++i)
    {
        res = PylonStreamGrabberDeregisterBuffer( hStreamGrabber, bufHandles[i] );
        CHECK( res );
        free( buffers[i] );
    }

    /* ... Release grabbing related resources. */
    res = PylonStreamGrabberFinishGrab( hStreamGrabber );
    CHECK( res );

    /* After calling PylonStreamGrabberFinishGrab(), parameters that impact the payload size (e.g.,
    the AOI width and height parameters) are unlocked and can be modified again. */

    /* ... Close the stream grabber. */
    res = PylonStreamGrabberClose( hStreamGrabber );
    CHECK( res );

    /* ... Deregister the callback. */
    res = GenApiNodeDeregisterCallback( hNode, hCallback );
    CHECK( res );

    /* ... Close the event grabber.*/
    res = PylonEventGrabberClose( hEventGrabber );
    CHECK( res );

    /* ... Release the event adapter. */
    res = PylonDeviceDestroyEventAdapter( hDev, hEventAdapter );
    CHECK( res );

    /* ... Release the wait object container. */
    res = PylonWaitObjectsDestroy( hWaitObjects );
    CHECK( res );


    /* ... Close and release the pylon device. The stream grabber becomes invalid
    after closing the pylon device. Don't call stream grabber related methods after
    closing or releasing the device. */
    res = PylonDeviceClose( hDev );
    CHECK( res );
    res = PylonDestroyDevice( hDev );
    CHECK( res );


    /* ... Shut down the pylon runtime system. Don't call any pylon method after
    calling PylonTerminate(). */
    PylonTerminate();
    pressEnterToExit();

    return EXIT_SUCCESS;
}

/* This function demonstrates how to retrieve the error message for the last failed
   function call. */
void printErrorAndExit( GENAPIC_RESULT errc )
{
    char* errMsg;
    size_t length;

    /* Retrieve the error message.
    ... First find out how big the buffer must be, */
    GenApiGetLastErrorMessage( NULL, &length );
    errMsg = (char*) malloc( length );
    /* ... and retrieve the message. */
    GenApiGetLastErrorMessage( errMsg, &length );

    fprintf( stderr, "%s (%#08x).\n", errMsg, (unsigned int) errc );
    free( errMsg );

    /* Retrieve more details about the error
    ... First find out how big the buffer must be, */
    GenApiGetLastErrorDetail( NULL, &length );
    errMsg = (char*) malloc( length );
    /* ... and retrieve the message. */
    GenApiGetLastErrorDetail( errMsg, &length );

    fprintf( stderr, "%s\n", errMsg );
    free( errMsg );

    PylonTerminate();  /* Releases all pylon resources. */
    pressEnterToExit();

    exit( EXIT_FAILURE );
}



/* Simple "image processing" function returning the minumum and maximum gray
value of an 8 bit gray value image. */
void getMinMax( const unsigned char* pImg, int32_t width, int32_t height,
                unsigned char* pMin, unsigned char* pMax )
{
    unsigned char min = 255;
    unsigned char max = 0;
    unsigned char val;
    const unsigned char* p;

    for (p = pImg; p < pImg + width * height; p++)
    {
        val = *p;
        if (val > max)
            max = val;
        if (val < min)
            min = val;
    }
    *pMin = min;
    *pMax = max;
}

/* Callback will be fired when an event message contains an end-of-exposure event. */
void GENAPIC_CC endOfExposureCallback( NODE_HANDLE hNode )
{
    int64_t frame;
    GENAPIC_RESULT res;
    res = GenApiIntegerGetValue( hNode, &frame );
    CHECK( res );

#if __STDC_VERSION__ >= 199901L || defined(__GNUC__)
    printf( "Got end-of-exposure event. Frame number: %lld\n", (long long) frame );
#else
    printf( "Got end-of-exposure event. Frame number: %I64d\n", frame );
#endif
}

/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void )
{
    fprintf( stderr, "\nPress enter to exit.\n" );
    while (getchar() != '\n');
}

GenApiParam Sample#

/*
  This sample illustrates how to access the different camera
  parameter types. It uses the low-level functions provided by GenApiC
  instead of those provided by pylonC.
*/

#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>

#include <pylonc/PylonC.h>

#define STRING_BUFFER_SIZE  512

#define CHECK(errc) if (GENAPI_E_OK != errc) printErrorAndExit(errc)

/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void );

/* This function demonstrates how to retrieve the error message for the last failed
   function call. */
static void printErrorAndExit( GENAPIC_RESULT errc )
{
    char* errMsg;
    size_t length;

    /* Retrieve the error message.
    ... First find out how big the buffer must be, */
    GenApiGetLastErrorMessage( NULL, &length );
    errMsg = (char*) malloc( length );
    /* ... and retrieve the message. */
    GenApiGetLastErrorMessage( errMsg, &length );

    fprintf( stderr, "%s (%#08x).\n", errMsg, (unsigned int) errc );
    free( errMsg );

    /* Retrieve more details about the error
    ... First find out how big the buffer must be, */
    GenApiGetLastErrorDetail( NULL, &length );
    errMsg = (char*) malloc( length );
    /* ... and retrieve the message. */
    GenApiGetLastErrorDetail( errMsg, &length );

    fprintf( stderr, "%s\n", errMsg );
    free( errMsg );

    PylonTerminate();  /* Releases all pylon resources. */
    pressEnterToExit();

    exit( EXIT_FAILURE );
}

static void SetMigrationModeEnable( PYLON_DEVICE_HANDLE hDev )
{
    NODEMAP_HANDLE  hNodeMap;
    NODE_HANDLE     hNode;
    const char*     pFeatureName = "MigrationModeEnable";   /* The name of the feature. */
    _Bool           value, bval;                            /* The value of the feature. */
    EGenApiNodeType nodeType;
    GENAPIC_RESULT  res;

    /* Get a handle for the device's node map. */
    res = PylonDeviceGetTLNodeMap( hDev, &hNodeMap );
    CHECK( res );

    /* Look up the feature node. */
    res = GenApiNodeMapGetNode( hNodeMap, pFeatureName, &hNode );
    CHECK( res );
    if (GENAPIC_INVALID_HANDLE == hNode)
    {
        fprintf( stderr, "There is no feature named %s.\n", pFeatureName );
        return;
    }

    /* We want a boolean feature node. */
    res = GenApiNodeGetType( hNode, &nodeType );
    CHECK( res );

    if (BooleanNode != nodeType)
    {
        fprintf( stderr, "'%s' is not a boolean feature.", pFeatureName );
        return;
    }

    /* Check to see if the feature is readable. */
    res = GenApiNodeIsReadable( hNode, &bval );
    CHECK( res );
    if (bval)
    {
        /* Retrieve the current state of the feature. */
        res = GenApiBooleanGetValue( hNode, &value );
        CHECK( res );
        fprintf( stdout, "The '%s' feature is %s.", pFeatureName, value ? "on" : "off" );

        /* Set a new value. */
        res = GenApiNodeIsWritable( hNode, &bval );
        CHECK( res );

        if (bval)
        {
            value = 1;  /* New value to turn feature on. */
            fprintf( stdout, "Switching the %s feature %s.\n", pFeatureName, value ? "on" : "off" );
            res = GenApiBooleanSetValue( hNode, value );
            CHECK( res );
        }
        else
        {
            fprintf( stderr, "Cannot set value for feature '%s' - node not writable.\n", pFeatureName );
        }
    }
    else
    {
        fprintf( stderr, "Cannot read feature '%s' - node not readable.\n", pFeatureName );
    }
}

/* This function demonstrates how to check the presence, readability, and writability
   of a feature. */
static void demonstrateAccessibilityCheck( PYLON_DEVICE_HANDLE hDev )
{
    NODEMAP_HANDLE      hNodeMap;
    NODE_HANDLE         hNode;
    const char*         pFeatureName;
    _Bool               val, val_read, val_write;
    GENAPIC_RESULT      res;

    /* Get a handle for the device's node map. */
    res = PylonDeviceGetNodeMap( hDev, &hNodeMap );
    CHECK( res );

    /* Check to see if a feature is implemented at all. The 'Width' feature is likely to
    be implemented by just about every existing camera. */
    pFeatureName = "Width";
    res = GenApiNodeMapGetNode( hNodeMap, pFeatureName, &hNode );
    CHECK( res );
    if (GENAPIC_INVALID_HANDLE != hNode)
    {
        /* Node exists, check whether feature is implemented. */
        res = GenApiNodeIsImplemented( hNode, &val );
        CHECK( res );
    }
    else
    {
        /* Node does not exist --> feature is not implemented. */
        val = 0;
    }
    printf( "The '%s' feature %s implemented\n", pFeatureName, val ? "is" : "is not" );

    /* This feature most likely does not exist */
    pFeatureName = "Weirdness";
    res = GenApiNodeMapGetNode( hNodeMap, pFeatureName, &hNode );
    CHECK( res );
    if (GENAPIC_INVALID_HANDLE != hNode)
    {
        /* Node exists, check whether feature is implemented. */
        res = GenApiNodeIsImplemented( hNode, &val );
        CHECK( res );
    }
    else
    {
        /* Node does not exist --> feature is not implemented. */
        val = 0;
    }
    printf( "The '%s' feature %s implemented\n", pFeatureName, val ? "is" : "is not" );


    /* Although a feature is implemented by the device, it may not be available
       with the device in its current state. Check to see if the feature is currently
       available. The GenApiNodeIsAvailable sets val to 0 if either the feature
       is not implemented or if the feature is not currently available. */
    pFeatureName = "BinningVertical";
    res = GenApiNodeMapGetNode( hNodeMap, pFeatureName, &hNode );
    CHECK( res );
    if (GENAPIC_INVALID_HANDLE != hNode)
    {
        /* Node exists, check whether feature is available. */
        res = GenApiNodeIsAvailable( hNode, &val );
        CHECK( res );
    }
    else
    {
        /* Node does not exist --> feature is not implemented, and hence not available. */
        val = 0;
    }
    printf( "The '%s' feature %s available\n", pFeatureName, val ? "is" : "is not" );

    /* If a feature is available, it could be read-only, write-only, or both
       readable and writable. Use the GenApiNodeIsReadable() and the
       GenApiNodeIsReadable() functions(). It is safe to call these functions
       for features that are currently not available or not implemented by the device.
       A feature that is not available or not implemented is neither readable nor writable.
       The readability and writability of a feature can change depending on the current
       state of the device. For example, the Width parameter might not be writable when
       the camera is acquiring images. */

    pFeatureName = "Width";
    res = GenApiNodeMapGetNode( hNodeMap, pFeatureName, &hNode );
    CHECK( res );
    if (GENAPIC_INVALID_HANDLE != hNode)
    {
        /* Node exists, check whether feature is readable. */
        res = GenApiNodeIsReadable( hNode, &val_read );
        CHECK( res );
        res = GenApiNodeIsReadable( hNode, &val_write );
        CHECK( res );
    }
    else
    {
        /* Node does not exist --> feature is neither readable nor witable. */
        val_read = val_write = 0;
    }
    printf( "The '%s' feature %s readable\n", pFeatureName, val_read ? "is" : "is not" );
    printf( "The '%s' feature %s writable\n", pFeatureName, val_write ? "is" : "is not" );
    printf( "\n" );
}


/* This function demonstrates how to handle integer camera parameters. */
static void demonstrateIntFeature( PYLON_DEVICE_HANDLE hDev )
{
    NODEMAP_HANDLE      hNodeMap;
    NODE_HANDLE         hNode;
    static const char   featureName[] = "Width";  /* Name of the feature used in this sample: AOI Width. */
    int64_t             val, min, max, incr;      /* Properties of the feature. */
    GENAPIC_RESULT      res;                      /* Return value. */
    EGenApiNodeType     nodeType;
    _Bool               bval;

    /* Get a handle for the device's node map. */
    res = PylonDeviceGetNodeMap( hDev, &hNodeMap );
    CHECK( res );

    /* Look up the feature node */
    res = GenApiNodeMapGetNode( hNodeMap, featureName, &hNode );
    CHECK( res );
    if (GENAPIC_INVALID_HANDLE == hNode)
    {
        fprintf( stderr, "There is no feature named '%s'\n", featureName );
        return;
    }

    /* We want an integer feature node. */
    res = GenApiNodeGetType( hNode, &nodeType );
    CHECK( res );
    if (IntegerNode != nodeType)
    {
        fprintf( stderr, "'%s' is not an integer feature\n", featureName );
        return;
    }

    /*
       Query the current value, the range of allowed values, and the increment of the feature.
       For some integer features, you are not allowed to set every value within the
       value range. For example, for some cameras the Width parameter must be a multiple
       of 2. These constraints are expressed by the increment value. Valid values
       follow the rule: val >= min && val <= max && val == min + n * inc.
    */

    res = GenApiNodeIsReadable( hNode, &bval );
    CHECK( res );

    if (bval)
    {
        res = GenApiIntegerGetMin( hNode, &min );       /* Get the minimum value. */
        CHECK( res );
        res = GenApiIntegerGetMax( hNode, &max );       /* Get the maximum value. */
        CHECK( res );
        res = GenApiIntegerGetInc( hNode, &incr );       /* Get the increment value. */
        CHECK( res );
        res = GenApiIntegerGetValue( hNode, &val );     /* Get the current value. */
        CHECK( res );

#if __STDC_VERSION__ >= 199901L || defined(__GNUC__)
        printf( "%s: min= %lld  max= %lld  incr=%lld  Value=%lld\n", featureName, (long long) min, (long long) max, (long long) incr, (long long) val );
#else
        printf( "%s: min= %I64d  max= %I64d  incr=%I64d  Value=%I64d\n", featureName, min, max, incr, val );
#endif

        res = GenApiNodeIsWritable( hNode, &bval );
        CHECK( res );

        if (bval)
        {
            /* Set the Width half-way between minimum and maximum. */
            res = GenApiIntegerSetValue( hNode, min + (max - min) / incr / 2 * incr );
            CHECK( res );
        }
        else
            fprintf( stderr, "Cannot set value for feature '%s' - node not writable\n", featureName );
    }
    else
        fprintf( stderr, "Cannot read feature '%s' - node not readable\n", featureName );
}


/* Some features involve floating point parameters. This function illustrates how to set and get floating
   point parameters. */
static void demonstrateFloatFeature( PYLON_DEVICE_HANDLE hDev )
{
    NODEMAP_HANDLE      hNodeMap;
    NODE_HANDLE         hNode;
    static const char   featureName[] = "Gamma";  /* The name of the feature used. */
    _Bool               bval;                     /* Is the feature available? */
    double              min, max, value;          /* Value range and current value. */
    EGenApiNodeType     nodeType;
    GENAPIC_RESULT      res;                      /* Return value. */

    /* Get a handle for the device's node map */
    res = PylonDeviceGetNodeMap( hDev, &hNodeMap );
    CHECK( res );

    /* Look up the feature node. */
    res = GenApiNodeMapGetNode( hNodeMap, featureName, &hNode );
    CHECK( res );
    if (GENAPIC_INVALID_HANDLE == hNode)
    {
        fprintf( stderr, "There is no feature named '%s'\n", featureName );
        return;
    }

    /* We want a float feature node. */
    res = GenApiNodeGetType( hNode, &nodeType );
    CHECK( res );
    if (FloatNode != nodeType)
    {
        fprintf( stderr, "'%s' is not an floating-point feature\n", featureName );
        return;
    }

    res = GenApiNodeIsReadable( hNode, &bval );
    CHECK( res );

    if (bval)
    {
        /* Query the value range and the current value. */
        res = GenApiFloatGetMin( hNode, &min );
        CHECK( res );
        res = GenApiFloatGetMax( hNode, &max );
        CHECK( res );
        res = GenApiFloatGetValue( hNode, &value );
        CHECK( res );

        printf( "%s: min = %4.2f, max = %4.2f, value = %4.2f\n", featureName, min, max, value );

        /* Set a new value. */
        GenApiNodeIsWritable( hNode, &bval );
        CHECK( res );

        if (bval)
        {
            value = 0.5 * (min + max);
            printf( "Setting %s to %4.2f\n", featureName, value );
            res = GenApiFloatSetValue( hNode, value );
            CHECK( res );
        }
        else
            printf( "Cannot set value for feature '%s' - node not writable\n", featureName );
    }
    else
        printf( "Cannot read feature '%s' - node not readable\n", featureName );
}


/* Some features are boolean features that can be switched on and off.
   This function illustrates how to access boolean features. */
static void demonstrateBooleanFeature( PYLON_DEVICE_HANDLE hDev )
{
    NODEMAP_HANDLE      hNodeMap;
    NODE_HANDLE         hNode;
    static const char   featureName[] = "GammaEnable";  /* The name of the feature. */
    _Bool               value, bval;                    /* The value of the feature. */
    EGenApiNodeType     nodeType;
    GENAPIC_RESULT      res;                            /* Return value. */

    /* Get a handle for the device's node map. */
    res = PylonDeviceGetNodeMap( hDev, &hNodeMap );
    CHECK( res );

    /* Look up the feature node. */
    res = GenApiNodeMapGetNode( hNodeMap, featureName, &hNode );
    CHECK( res );
    if (GENAPIC_INVALID_HANDLE == hNode)
    {
        fprintf( stderr, "There is no feature named '%s'\n", featureName );
        return;
    }

    /* We want a boolean feature node. */
    res = GenApiNodeGetType( hNode, &nodeType );
    CHECK( res );
    if (BooleanNode != nodeType)
    {
        fprintf( stderr, "'%s' is not a boolean feature\n", featureName );
        return;
    }

    /* Check to see if the feature is readable. */
    res = GenApiNodeIsReadable( hNode, &bval );
    CHECK( res );

    if (bval)
    {
        /* Retrieve the current state of the feature. */
        res = GenApiBooleanGetValue( hNode, &value );
        CHECK( res );
        printf( "The %s features is %s\n", featureName, value ? "on" : "off" );

        /* Set a new value. */
        GenApiNodeIsWritable( hNode, &bval );
        CHECK( res );

        if (bval)
        {
            value = (_Bool) !value;  /* New value */
            printf( "Switching the %s feature %s\n", featureName, value ? "on" : "off" );
            res = GenApiBooleanSetValue( hNode, value );
            CHECK( res );
        }
        else
            printf( "Cannot set value for feature '%s' - node not writable\n", featureName );
    }
    else
        printf( "Cannot read feature '%s' - node not readable\n", featureName );
}


/*
  Regardless of the parameter's type, any parameter value can be retrieved as a string. Each parameter
  can be set by passing in a string correspondingly. This function illustrates how to set and get the
  Width parameter as string. As demonstrated above, the Width parameter is of the integer type.
  */
static void demonstrateFromStringToString( PYLON_DEVICE_HANDLE hDev )
{
    static const char   featureName[] = "Width";   /* The name of the feature. */

    NODEMAP_HANDLE      hNodeMap;
    NODE_HANDLE         hNode;
    EGenApiNodeType     nodeType;
    _Bool               bIsReadable;
    GENAPIC_RESULT      res;                       /* Return value. */

    /* Get a handle for the device's node map */
    res = PylonDeviceGetNodeMap( hDev, &hNodeMap );
    CHECK( res );

    /* Look up the feature node. */
    res = GenApiNodeMapGetNode( hNodeMap, featureName, &hNode );
    CHECK( res );
    if (GENAPIC_INVALID_HANDLE == hNode)
    {
        fprintf( stderr, "There is no feature named '%s'\n", featureName );
        return;
    }

    /* We want an integer feature node. */
    res = GenApiNodeGetType( hNode, &nodeType );
    CHECK( res );
    if (IntegerNode != nodeType)
    {
        fprintf( stderr, "'%s' is not an integer feature\n", featureName );
        return;
    }

    /* Check to see if the feature is readable. */
    res = GenApiNodeIsReadable( hNode, &bIsReadable );
    CHECK( res );

    if (bIsReadable)
    {
        size_t len;
        char* buf, fixBuf[32];
        _Bool bIsWritable;

        /* Get the value of a feature as a string. Normally, getting the value consists of 3 steps:
           1.) Determine the required buffer size.
           2.) Allocate the buffer.
           3.) Retrieve the value. */
        /* ... Get the required buffer size. The size is queried by
               passing a NULL pointer as a pointer to the buffer. */
        res = GenApiNodeToString( hNode, NULL, &len );
        CHECK( res );
        /* ... len is set to the required buffer size (terminating zero included).
               Allocate the memory and retrieve the string. */
        buf = (char*) malloc( len );
        res = GenApiNodeToString( hNode, buf, &len );
        CHECK( res );

        printf( "%s: %s\n", featureName, buf );
        free( buf );

        /* You are not necessarily required to query the buffer size in advance. If the buffer is
           big enough, passing in a buffer and a pointer to its length will work.
           When the buffer is too small, an error is returned. */

        /* Passing in a buffer that is too small. */
        len = 1;
        res = GenApiNodeToString( hNode, fixBuf, &len );
        if (res == GENAPI_E_INSUFFICIENT_BUFFER)
        {
            /* The buffer was too small. The required size is indicated by len. */
            printf( "Buffer is too small for the value of '%s'. The required buffer size is %d\n", featureName, (int) len );
        }
        else
            CHECK( res );  /* Unexpected return value. */

        /* Passing in a buffer with sufficient size. */
        len = sizeof fixBuf;
        res = GenApiNodeToString( hNode, fixBuf, &len );
        CHECK( res );


        /* A feature can be set as a string using the GenApiNodeFromString() function.
           If the content of a string can not be converted to the type of the feature, an
           error is returned. */
        GenApiNodeIsWritable( hNode, &bIsWritable );
        CHECK( res );

        if (bIsWritable)
        {
            res = GenApiNodeFromString( hNode, "fourty-two" ); /* Can not be converted to an integer. */
            if (res != GENAPI_E_OK)
            {
                /* Print out an error message. */
                size_t l;
                char* msg;
                GenApiGetLastErrorMessage( NULL, &l ); /* Retrieve buffer size for the error message. */
                msg = (char*) malloc( l );             /* Provide memory. */
                GenApiGetLastErrorMessage( msg, &l );  /* Retrieve the message. */
                printf( "%s\n", msg );
                free( msg );
            }
        }
        else
            printf( "Cannot set value for feature '%s' - node not writable\n", featureName );
    }
    else
        printf( "Cannot read feature '%s' - node not readable\n", featureName );
}


/* There are camera features that behave like enumerations. These features can take a value from a fixed
   set of possible values. One example is the pixel format feature. This function illustrates how to deal with
   enumeration features.

*/
static void demonstrateEnumFeature( PYLON_DEVICE_HANDLE hDev )
{
    static const char   featureName[] = "PixelFormat";
    NODEMAP_HANDLE      hNodeMap;
    NODE_HANDLE         hNode;
    EGenApiNodeType     nodeType;
    _Bool               bval;
    GENAPIC_RESULT      res;                           /* Return value. */


    /* Get a handle for the device's node map. */
    res = PylonDeviceGetNodeMap( hDev, &hNodeMap );
    CHECK( res );

    /* Look up the feature node. */
    res = GenApiNodeMapGetNode( hNodeMap, featureName, &hNode );
    CHECK( res );
    if (GENAPIC_INVALID_HANDLE == hNode)
    {
        fprintf( stderr, "There is no feature named '%s'\n", featureName );
        return;
    }

    /* We want an enumeration feature node. */
    res = GenApiNodeGetType( hNode, &nodeType );
    CHECK( res );
    if (EnumerationNode != nodeType)
    {
        fprintf( stderr, "'%s' is not an enumeration feature\n", featureName );
        return;
    }

    /* Check to see if the feature is readable. */
    res = GenApiNodeIsReadable( hNode, &bval );
    CHECK( res );

    /* The allowed values for an enumeration feature are represented as strings. Use the
    GenApiNodeFromString() and GenApiNodeToString() methods for setting and getting
    the value of an enumeration feature. */

    if (bval)
    {
        /* Symbolic names of pixel formats. */
        static const char symMono8[] = "Mono8";
        static const char symMono16[] = "Mono16";
        static const char symYUV422Packed[] = "YUV422Packed";

        size_t      len;                           /* The length of the string. */
        char        value[64];                     /* The current value of the feature. */
        _Bool       supportsMono8, supportsYUV422Packed, supportsMono16;
        NODE_HANDLE hEntry;


        /* Get the current value of the enumeration feature. */
        len = sizeof value;
        res = GenApiNodeToString( hNode, value, &len );
        CHECK( res );

        printf( "PixelFormat: %s\n", value );

        /*
        For an enumeration feature, the pylon Viewer's "Feature Documentation" window lists the
        names of the possible values. Some of the values may not be supported by the device.
        To check if a certain "SomeValue" value for a "SomeFeature" feature can be set, call the
        GenApiNodeIsAvailable() on the node of the enum entry.
        */

        /* Check to see if the Mono8 pixel format can be set. */
        res = GenApiEnumerationGetEntryByName( hNode, symMono8, &hEntry );
        CHECK( res );
        if (hEntry != GENAPIC_INVALID_HANDLE)
        {
            res = GenApiNodeIsAvailable( hEntry, &supportsMono8 );
            CHECK( res );
        }
        else
        {
            supportsMono8 = 0;
        }
        printf( "%s %s a supported value for the PixelFormat feature\n", symMono8, supportsMono8 ? "is" : "is not" );

        /* Check to see if the YUV422Packed pixel format can be set. */
        res = GenApiEnumerationGetEntryByName( hNode, symYUV422Packed, &hEntry );
        CHECK( res );
        if (hEntry != GENAPIC_INVALID_HANDLE)
        {
            res = GenApiNodeIsAvailable( hEntry, &supportsYUV422Packed );
            CHECK( res );
        }
        else
        {
            supportsYUV422Packed = 0;
        }
        printf( "%s %s a supported value for the PixelFormat feature\n", symYUV422Packed, supportsYUV422Packed ? "is" : "is not" );

        /* Check to see if the Mono16 pixel format can be set. */
        res = GenApiEnumerationGetEntryByName( hNode, symMono16, &hEntry );
        CHECK( res );
        if (hEntry != GENAPIC_INVALID_HANDLE)
        {
            res = GenApiNodeIsAvailable( hEntry, &supportsMono16 );
            CHECK( res );
        }
        else
        {
            supportsMono16 = 0;
        }
        printf( "%s %s a supported value for the PixelFormat feature\n", symMono16, supportsMono16 ? "is" : "is not" );


        /* Before writing a value, we recommend checking if the enumeration feature is
        currently writable. */
        res = GenApiNodeIsWritable( hNode, &bval );
        CHECK( res );

        if (bval)
        {
            /* The PixelFormat feature is writable, set it to one of the supported values. */
            if (supportsMono16)
            {
                printf( "Setting PixelFormat to Mono16\n" );
                res = GenApiNodeFromString( hNode, symMono16 );
                CHECK( res );
            }
            else if (supportsYUV422Packed)
            {
                printf( "Setting PixelFormat to YUV422Packed\n" );
                res = GenApiNodeFromString( hNode, symYUV422Packed );
                CHECK( res );
            }
            else if (supportsMono8)
            {
                printf( "Setting PixelFormat to Mono8\n" );
                res = GenApiNodeFromString( hNode, symMono8 );
                CHECK( res );
            }

            /* Reset the PixelFormat feature to its previous value. */
            res = GenApiNodeFromString( hNode, value );
            CHECK( res );
        }
        else
            printf( "Cannot set value for feature '%s' - node not writable\n", featureName );
    }
    else
        printf( "Cannot read feature '%s' - node not readable\n", featureName );
}


/* Enumerate all possible entries for an enumerated feature. For every entry, a selection
   of properties is displayed. A loop similar to the one shown below may be part of a
   GUI program that wants to fill the entries of a menu. */
static void demonstrateEnumIteration( PYLON_DEVICE_HANDLE hDev )
{
    static const char   featureName[] = "PixelFormat";
    NODEMAP_HANDLE      hNodeMap;
    NODE_HANDLE         hNode;
    EGenApiNodeType     nodeType;
    _Bool               bval;
    GENAPIC_RESULT      res;                           /* Return value. */


    /* Get a handle for the device's node map */
    res = PylonDeviceGetNodeMap( hDev, &hNodeMap );
    CHECK( res );

    /* Look up the feature node. */
    res = GenApiNodeMapGetNode( hNodeMap, featureName, &hNode );
    CHECK( res );
    if (GENAPIC_INVALID_HANDLE == hNode)
    {
        fprintf( stderr, "There is no feature named '%s'\n", featureName );
        return;
    }

    /* We want an enumeration feature node. */
    res = GenApiNodeGetType( hNode, &nodeType );
    CHECK( res );
    if (EnumerationNode != nodeType)
    {
        fprintf( stderr, "'%s' is not an enumeration feature\n", featureName );
        return;
    }

    /* Check to see if the feature is readable. */
    res = GenApiNodeIsReadable( hNode, &bval );
    CHECK( res );

    if (bval)
    {
        size_t max, i;

        /* check entries. */
        res = GenApiEnumerationGetNumEntries( hNode, &max );
        CHECK( res );

        /* Write out header. */
        printf( "Allowed values for feature '%s':\n"
                "--------------\n",
                featureName );

         /* A loop to visit every enumeration entry node once. */
        for (i = 0; i < max; i++)
        {
            NODE_HANDLE hEntry;
            char name[128], displayName[STRING_BUFFER_SIZE], description[STRING_BUFFER_SIZE];
            size_t siz;
            _Bool avail;

            /* Get handle for enumeration entry node. */
            res = GenApiEnumerationGetEntryByIndex( hNode, i, &hEntry );
            CHECK( res );

            /* Get node name. */
            siz = sizeof name;
            res = GenApiNodeGetName( hEntry, name, &siz );
            CHECK( res );

            /* Get display name. */
            siz = sizeof displayName;
            res = GenApiNodeGetDisplayName( hEntry, displayName, &siz );
            CHECK( res );

            /* Get description. */
            siz = sizeof description;
            res = GenApiNodeGetDescription( hEntry, description, &siz );
            CHECK( res );

            /* Get availability. */
            res = GenApiNodeIsAvailable( hEntry, &avail );
            CHECK( res );

            /* Write out results. */
            printf( "Node name:    %s\n"
                    "Display name: %s\n"
                    "Description:  %s\n"
                    "Available:    %s\n"
                    "--------------\n",
                    name, displayName, description, avail ? "yes" : "no" );
        }
    }
    else
        printf( "Cannot read feature '%s' - node not readable\n", featureName );
}



/* Traverse the feature tree, displaying all categories and all features. */
static void handleCategory( NODE_HANDLE hRoot, char* buf, unsigned int depth )
{
    GENAPIC_RESULT      res;
    size_t              bufsiz, siz, numfeat, i;

    /* Write out node name. */
    siz = bufsiz = STRING_BUFFER_SIZE - depth * 2;
    res = GenApiNodeGetName( hRoot, buf, &siz );
    CHECK( res );

    /* Get the number of feature nodes in this category. */
    res = GenApiCategoryGetNumFeatures( hRoot, &numfeat );
    CHECK( res );

    printf( "%s category has %u children\n", buf - depth * 2, (unsigned int) numfeat );


    /* Increase indentation. */
    *buf++ = ' ';
    *buf++ = ' ';
    bufsiz -= 2;
    ++depth;

    /* Now loop over all feature nodes. */
    for (i = 0; i < numfeat; ++i)
    {
        NODE_HANDLE         hNode;
        EGenApiNodeType     nodeType;

        /* Get next feature node and check its type. */
        res = GenApiCategoryGetFeatureByIndex( hRoot, i, &hNode );
        CHECK( res );
        res = GenApiNodeGetType( hNode, &nodeType );
        CHECK( res );

        if (Category != nodeType)
        {
            /* A regular feature. */
            EGenApiAccessMode am;
            const char* amode;

            siz = bufsiz;
            res = GenApiNodeGetName( hNode, buf, &siz );
            CHECK( res );
            res = GenApiNodeGetAccessMode( hNode, &am );
            CHECK( res );

            switch (am)
            {
                case NI:
                    amode = "not implemented";
                    break;
                case NA:
                    amode = "not available";
                    break;
                case WO:
                    amode = "write only";
                    break;
                case RO:
                    amode = "read only";
                    break;
                case RW:
                    amode = "read and write";
                    break;
                default:
                    amode = "undefined";
                    break;
            }

            printf( "%s feature - access: %s\n", buf - depth * 2, amode );
        }
        else
            /* Another category node. */
            handleCategory( hNode, buf, depth );
    }
}

static void demonstrateCategory( PYLON_DEVICE_HANDLE hDev )
{
    NODEMAP_HANDLE      hNodeMap;
    NODE_HANDLE         hNode;
    char                buf[512];
    GENAPIC_RESULT      res;

    /* Get a handle for the device's node map. */
    res = PylonDeviceGetNodeMap( hDev, &hNodeMap );
    CHECK( res );

    /* Look up the root node. */
    res = GenApiNodeMapGetNode( hNodeMap, "Root", &hNode );
    CHECK( res );

    handleCategory( hNode, buf, 0 );
}



/* There are camera features, such as starting image acquisition, that represent a command.
   This function that loads the factory settings, illustrates how to execute a command feature.  */
static void demonstrateCommandFeature( PYLON_DEVICE_HANDLE hDev )
{
    static const char   selectorName[] = "UserSetSelector";
    static const char   commandName[] = "UserSetLoad";
    NODEMAP_HANDLE      hNodeMap;
    NODE_HANDLE         hCommand, hSelector;
    EGenApiNodeType     nodeType;
    _Bool               bval;
    GENAPIC_RESULT      res;  /* Return value. */

    /* Get a handle for the device's node map. */
    res = PylonDeviceGetNodeMap( hDev, &hNodeMap );
    CHECK( res );

    /* Look up the command node. */
    res = GenApiNodeMapGetNode( hNodeMap, commandName, &hCommand );
    CHECK( res );
    if (GENAPIC_INVALID_HANDLE == hCommand)
    {
        fprintf( stderr, "There is no feature named '%s'\n", commandName );
        return;
    }

    /* Look up the selector node. */
    res = GenApiNodeMapGetNode( hNodeMap, selectorName, &hSelector );
    CHECK( res );
    if (GENAPIC_INVALID_HANDLE == hSelector)
    {
        fprintf( stderr, "There is no feature named '%s'\n", selectorName );
        return;
    }

    /* We want a command feature node. */
    res = GenApiNodeGetType( hCommand, &nodeType );
    CHECK( res );
    if (CommandNode != nodeType)
    {
        fprintf( stderr, "'%s' is not a command feature\n", selectorName );
        return;
    }

    /* Before executing the user set load command, the configuration set selector must be
       set to the default set. */

    /* Check to see if the selector is writable. */
    res = GenApiNodeIsWritable( hSelector, &bval );
    CHECK( res );

    if (bval)
    {
        /* Choose the default configuration set (with one of the factory setups chosen). */
        res = GenApiNodeFromString( hSelector, "Default" );
        CHECK( res );
    }
    else
        printf( "Cannot set selector '%s' - node not writable\n", selectorName );


    /* Check to see if the command is writable. */
    res = GenApiNodeIsWritable( hCommand, &bval );
    CHECK( res );

    if (bval)
    {
        /* Execute the configuration set load command. */
        printf( "Loading the default set.\n" );
        res = GenApiCommandExecute( hCommand );
        CHECK( res );
    }
    else
        printf( "Cannot execute command '%s' - node not writable\n", commandName );
}


int
main( void )
{
    GENAPIC_RESULT              res;           /* Return value of pylon methods. */
    size_t                      numDevices;    /* Number of available devices. */
    PYLON_DEVICE_HANDLE         hDev;          /* Handle for the pylon device. */

    /* Before using any pylon methods, the pylon runtime must be initialized. */
    PylonInitialize();

    /* Enumerate all camera devices. You must call
    PylonEnumerateDevices() before creating a device. */
    res = PylonEnumerateDevices( &numDevices );
    CHECK( res );
    if (0 == numDevices)
    {
        fprintf( stderr, "No devices found.\n" );
        PylonTerminate();
        pressEnterToExit();
        exit( EXIT_FAILURE );
    }

    /* Get a handle for the first device found.  */
    res = PylonCreateDeviceByIndex( 0, &hDev );
    CHECK( res );

    /* Before using the device, it must be opened. Open it for configuring
    parameters and for grabbing images. */
    res = PylonDeviceOpen( hDev, PYLONC_ACCESS_MODE_CONTROL | PYLONC_ACCESS_MODE_STREAM );
    CHECK( res );

    /* Print out the name of the camera we are using. */
    {
        char buf[256];
        size_t siz = sizeof( buf );
        _Bool isReadable;

        isReadable = PylonDeviceFeatureIsReadable( hDev, "DeviceModelName" );
        if (isReadable)
        {
            res = PylonDeviceFeatureToString( hDev, "DeviceModelName", buf, &siz );
            CHECK( res );
            printf( "Using camera %s\n", buf );
        }
    }

    /* Switch on the MigrationModeEnable feature.  */
    SetMigrationModeEnable( hDev );

    /* Demonstrate how to check the accessibility of a feature. */
    demonstrateAccessibilityCheck( hDev );
    puts( "" );

    /* Demonstrate how to handle integer camera parameters. */
    demonstrateIntFeature( hDev );
    puts( "" );

    /* Demonstrate how to handle floating point camera parameters. */
    demonstrateFloatFeature( hDev );
    puts( "" );

    /* Demonstrate how to handle boolean camera parameters. */
    demonstrateBooleanFeature( hDev );
    puts( "" );

    /* Each feature can be read as a string and also set as a string. */
    demonstrateFromStringToString( hDev );
    puts( "" );

    /* Demonstrate how to handle enumeration camera parameters. */
    demonstrateEnumFeature( hDev );
    puts( "" );

    /* Demonstrate how to iterate enumeration entries. */
    demonstrateEnumIteration( hDev );
    puts( "" );

    /* Demonstrate how to execute actions. */
    demonstrateCommandFeature( hDev );
    puts( "" );

    /* Demonstrate category nodes. */
    demonstrateCategory( hDev );


    /* Clean up. Close and release the pylon device. */

    res = PylonDeviceClose( hDev );
    CHECK( res );
    res = PylonDestroyDevice( hDev );
    CHECK( res );


    /* Shut down the pylon runtime system. Don't call any pylon method after
       calling PylonTerminate(). */
    PylonTerminate();
    pressEnterToExit();

    return EXIT_SUCCESS;
}

/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void )
{
    fprintf( stderr, "\nPress enter to exit.\n" );
    while (getchar() != '\n');
}

GrabTwoCameras Sample#

/*
   This sample illustrates how to grab images and process images
   using multiple cameras simultaneously.

   The sample uses a pool of buffers that are passed to a stream grabber to be filled with
   image data. Once a buffer is filled and ready for processing, the buffer is retrieved from
   the stream grabber, processed, and passed back to the stream grabber to be filled again.
   Buffers retrieved from the stream grabber are not overwritten as long as
   they are not passed back to the stream grabber.
*/

#ifndef _WIN32_WINNT
#   define _WIN32_WINNT 0x0400
#endif

#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include <string.h>

#include <pylonc/PylonC.h>

#ifdef GENAPIC_LINUX_BUILD
#   include <sys/timerfd.h>
#   include <alloca.h>
#   include <errno.h>
#   include <unistd.h>
#endif

/* Limits the amount of cameras used for grabbing.
   It is important to manage the available bandwidth when grabbing with multiple
   cameras. This applies, for instance, if two GigE cameras are connected to the
   same network adapter via a switch. To manage the bandwidth, the GevSCPD
   interpacket delay parameter and the GevSCFTD transmission delay parameter can
   be set for each GigE camera device. The "Controlling Packet Transmission Timing
   with the Interpacket and Frame Transmission Delays on Basler GigE Vision Cameras"
   Application Note (AW000649xx000) provides more information about this topic. */
#define NUM_DEVICES 2
#define NUM_BUFFERS 5         /* Number of buffers used for grabbing. */

#define GIGE_PACKET_SIZE       1500 /* Size of one Ethernet packet. */
#define GIGE_PROTOCOL_OVERHEAD 36   /* Total number of bytes of protocol overhead. */

#define CHECK( errc ) if ( GENAPI_E_OK != errc ) printErrorAndExit( errc )

/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void );

/* This method demonstrates how to retrieve the error message for the last failed function call. */
void printErrorAndExit( GENAPIC_RESULT errc );

/* Calculating the minimum and maximum gray value. */
void getMinMax( const unsigned char* pImg, int32_t width, int32_t height,
                unsigned char* pMin, unsigned char* pMax );


int main( void )
{
    GENAPIC_RESULT              res;                      /* Return value of pylon methods. */
    size_t                      numDevicesAvail;          /* Number of available devices. */
    _Bool                       isAvail;                  /* Used for checking feature availability. */
    int                         deviceIndex;              /* Index of device used in following variables. */
    PYLON_WAITOBJECTS_HANDLE    wos;                      /* Wait objects. */
#ifdef GENAPIC_WIN_BUILD
    HANDLE                      hTimer;                   /* Grab timer. */
#else
    int                         fdTimer;                  /* Grab timer. */
#endif
    PYLON_WAITOBJECT_HANDLE     woTimer;                  /* Timer wait object. */

    /* These are camera specific variables */
    PYLON_DEVICE_HANDLE         hDev[NUM_DEVICES];        /* Handle for the pylon device. */
    PYLON_STREAMGRABBER_HANDLE  hGrabber[NUM_DEVICES];    /* Handle for the pylon stream grabber. */
    unsigned char*              buffers[NUM_DEVICES][NUM_BUFFERS]; /* Buffers used for grabbing. */
    PYLON_STREAMBUFFER_HANDLE   bufHandles[NUM_DEVICES][NUM_BUFFERS]; /* Handles for the buffers. */

    /* Before using any pylon methods, the pylon runtime must be initialized. */
    PylonInitialize();

    /* Enumerate all devices. You must call
    PylonEnumerateDevices() before creating a device. */
    res = PylonEnumerateDevices( &numDevicesAvail );
    CHECK( res );
    if (numDevicesAvail < NUM_DEVICES)
    {
        fprintf( stderr, "Not enough devices found. Found %u devices. At least %i devices needed to run this sample.\n", (unsigned int) numDevicesAvail, NUM_DEVICES );
        PylonTerminate();
        pressEnterToExit();
        exit( EXIT_SUCCESS );
    }

    /* Create wait objects (must be done outside of the loop). */
    res = PylonWaitObjectsCreate( &wos );
    CHECK( res );

    /* In this sample, we want to grab for a given amount of time, then stop. */
#ifdef GENAPIC_WIN_BUILD
    /* Create a Windows timer, wrap it in a pylon C wait object, and add it to
       the wait object set. */
    hTimer = CreateWaitableTimer( NULL, TRUE, NULL );
    if (hTimer == NULL)
    {
        fprintf( stderr, "CreateWaitableTimer() failed.\n" );
        PylonTerminate();
        pressEnterToExit();
        exit( EXIT_FAILURE );
    }
    res = PylonWaitObjectFromW32( hTimer, 0, &woTimer );
    CHECK( res );
#else
    /* Create a Linux timer, wrap it in a pylon C wait object, and add it to
       the wait object set. */
    fdTimer = timerfd_create( CLOCK_MONOTONIC, 0 );
    if (fdTimer == -1)
    {
        fprintf( stderr, "timerfd_create() failed. %s\n", strerror( errno ) );
        PylonTerminate();
        pressEnterToExit();
        exit( EXIT_FAILURE );
    }
    res = PylonWaitObjectFromFd( fdTimer, &woTimer );
    CHECK( res );
#endif

    res = PylonWaitObjectsAdd( wos, woTimer, NULL );
    CHECK( res );

    /* Open cameras and set parameters. */
    for (deviceIndex = 0; deviceIndex < NUM_DEVICES; ++deviceIndex)
    {
        PylonDeviceInfo_t di;

        /* Get a handle for the device. */
        res = PylonCreateDeviceByIndex( deviceIndex, &hDev[deviceIndex] );
        CHECK( res );

        /* Before using the device, it must be opened. Open it for setting
           parameters and for grabbing images. */
        res = PylonDeviceOpen( hDev[deviceIndex], PYLONC_ACCESS_MODE_CONTROL | PYLONC_ACCESS_MODE_STREAM );
        CHECK( res );

        /* Print out the name of the camera we are using. */
        {
            char buf[256];
            size_t siz = sizeof( buf );
            _Bool isReadable;

            isReadable = PylonDeviceFeatureIsReadable( hDev[deviceIndex], "DeviceModelName" );
            if (isReadable)
            {
                res = PylonDeviceFeatureToString( hDev[deviceIndex], "DeviceModelName", buf, &siz );
                CHECK( res );
                printf( "Using camera '%s'\n", buf );
            }
        }

        /* Set the pixel format to Mono8, where gray values will be output as 8 bit values for each pixel. */
        /* ... First check to see if the device supports the Mono8 format. */
        isAvail = PylonDeviceFeatureIsAvailable( hDev[deviceIndex], "EnumEntry_PixelFormat_Mono8" );
        if (!isAvail)
        {
            /* Feature is not available. */
            fprintf( stderr, "Device doesn't support the Mono8 pixel format" );
            PylonTerminate();
            pressEnterToExit();
            exit( EXIT_FAILURE );
        }

        /* ... Set the pixel format to Mono8. */
        res = PylonDeviceFeatureFromString( hDev[deviceIndex], "PixelFormat", "Mono8" );
        CHECK( res );

        /* Disable acquisition start trigger if available. */
        isAvail = PylonDeviceFeatureIsAvailable( hDev[deviceIndex], "EnumEntry_TriggerSelector_AcquisitionStart" );
        if (isAvail)
        {
            res = PylonDeviceFeatureFromString( hDev[deviceIndex], "TriggerSelector", "AcquisitionStart" );
            CHECK( res );
            res = PylonDeviceFeatureFromString( hDev[deviceIndex], "TriggerMode", "Off" );
            CHECK( res );
        }

        /* Disable frame burst start trigger if available. */
        isAvail = PylonDeviceFeatureIsAvailable( hDev[deviceIndex], "EnumEntry_TriggerSelector_FrameBurstStart" );
        if (isAvail)
        {
            res = PylonDeviceFeatureFromString( hDev[deviceIndex], "TriggerSelector", "FrameBurstStart" );
            CHECK( res );
            res = PylonDeviceFeatureFromString( hDev[deviceIndex], "TriggerMode", "Off" );
            CHECK( res );
        }

        /* Disable frame start trigger if available. */
        isAvail = PylonDeviceFeatureIsAvailable( hDev[deviceIndex], "EnumEntry_TriggerSelector_FrameStart" );
        if (isAvail)
        {
            res = PylonDeviceFeatureFromString( hDev[deviceIndex], "TriggerSelector", "FrameStart" );
            CHECK( res );
            res = PylonDeviceFeatureFromString( hDev[deviceIndex], "TriggerMode", "Off" );
            CHECK( res );
        }

        /* We will use the Continuous frame mode, i.e., the camera delivers images continuously. */
        res = PylonDeviceFeatureFromString( hDev[deviceIndex], "AcquisitionMode", "Continuous" );
        CHECK( res );


        res = PylonDeviceGetDeviceInfo( hDev[deviceIndex], &di );
        CHECK( res );
        if (strcmp( di.DeviceClass, "BaslerGigE" ) == 0)
        {
            /* For GigE cameras, we recommend increasing the packet size for better
               performance. When the network adapter supports jumbo frames, set the packet
               size to a value > 1500, e.g., to 8192. In this sample, we only set the packet size
               to 1500.

               Also we set the Inter-Packet and the Frame Transmission delay
               so the switch can line up packets better.
            */

            res = PylonDeviceSetIntegerFeature( hDev[deviceIndex], "GevSCPSPacketSize", GIGE_PACKET_SIZE );
            CHECK( res );

            res = PylonDeviceSetIntegerFeature( hDev[deviceIndex], "GevSCPD", (GIGE_PACKET_SIZE + GIGE_PROTOCOL_OVERHEAD) * (NUM_DEVICES - 1) );
            CHECK( res );

            res = PylonDeviceSetIntegerFeature( hDev[deviceIndex], "GevSCFTD", (GIGE_PACKET_SIZE + GIGE_PROTOCOL_OVERHEAD) * deviceIndex );
            CHECK( res );
        }
    }


    /* Allocate and register buffers for grab. */
    for (deviceIndex = 0; deviceIndex < NUM_DEVICES; ++deviceIndex)
    {
        size_t i;
        PYLON_WAITOBJECT_HANDLE hWait;
        size_t payloadSize;


        /* Image grabbing is done using a stream grabber.
           A device may be able to provide different streams. A separate stream grabber must
           be used for each stream. In this sample, we create a stream grabber for the default
           stream, i.e., the first stream ( index == 0 ). */

        /* Get the number of streams supported by the device and the transport layer. */
        res = PylonDeviceGetNumStreamGrabberChannels( hDev[deviceIndex], &i );
        CHECK( res );
        if (i < 1)
        {
            fprintf( stderr, "The transport layer doesn't support image streams.\n" );
            PylonTerminate();
            pressEnterToExit();
            exit( EXIT_FAILURE );
        }

        /* Create and open a stream grabber for the first channel. */
        res = PylonDeviceGetStreamGrabber( hDev[deviceIndex], 0, &hGrabber[deviceIndex] );
        CHECK( res );
        res = PylonStreamGrabberOpen( hGrabber[deviceIndex] );
        CHECK( res );

        /* Get a handle for the stream grabber's wait object. The wait object
           allows waiting for buffers to be filled with grabbed data. */
        res = PylonStreamGrabberGetWaitObject( hGrabber[deviceIndex], &hWait );
        CHECK( res );

        /* Add the stream grabber's wait object to our wait objects.
           This is needed to be able to wait until at least one camera has
           grabbed an image in the grab loop below. */
        res = PylonWaitObjectsAdd( wos, hWait, NULL );
        CHECK( res );


        res = PylonStreamGrabberGetPayloadSize( hDev[deviceIndex], hGrabber[deviceIndex], &payloadSize );
        CHECK( res );

        /* Allocate memory for grabbing.  */
        for (i = 0; i < NUM_BUFFERS; ++i)
        {
            buffers[deviceIndex][i] = (unsigned char*) malloc( payloadSize );
            if (NULL == buffers[deviceIndex][i])
            {
                fprintf( stderr, "Out of memory.\n" );
                PylonTerminate();
                pressEnterToExit();
                exit( EXIT_FAILURE );
            }
        }

        /* We must tell the stream grabber the number and size of the buffers we are using. */
        /* .. We will not use more than NUM_BUFFERS for grabbing. */
        res = PylonStreamGrabberSetMaxNumBuffer( hGrabber[deviceIndex], NUM_BUFFERS );
        CHECK( res );
        /* .. We will not use buffers bigger than payloadSize bytes. */
        res = PylonStreamGrabberSetMaxBufferSize( hGrabber[deviceIndex], payloadSize );
        CHECK( res );


        /* Allocate the resources required for grabbing. After this, critical parameters
           that impact the payload size must not be changed until FinishGrab() is called. */
        res = PylonStreamGrabberPrepareGrab( hGrabber[deviceIndex] );
        CHECK( res );


        /* Before using the buffers for grabbing, they must be registered at
           the stream grabber. For each registered buffer, a buffer handle
           is returned. After registering, these handles are used instead of the
           raw pointers. */
        for (i = 0; i < NUM_BUFFERS; ++i)
        {
            res = PylonStreamGrabberRegisterBuffer( hGrabber[deviceIndex], buffers[deviceIndex][i], payloadSize, &bufHandles[deviceIndex][i] );
            CHECK( res );
        }

        /* Feed the buffers into the stream grabber's input queue. For each buffer, the API
           allows passing in a pointer to additional context information. This pointer
           will be returned unchanged when the grab is finished. In our example, we use the index of the
           buffer as context information. */
        for (i = 0; i < NUM_BUFFERS; ++i)
        {
            res = PylonStreamGrabberQueueBuffer( hGrabber[deviceIndex], bufHandles[deviceIndex][i], (void*) i );
            CHECK( res );
        }
    }


    /* The stream grabber is now prepared. As soon the camera starts to acquire images,
       the image data will be grabbed into the provided buffers.  */
    for (deviceIndex = 0; deviceIndex < NUM_DEVICES; ++deviceIndex)
    {
        /* Start the image acquisition engine. */
        res = PylonStreamGrabberStartStreamingIfMandatory( hGrabber[deviceIndex] );
        /* do not call CHECK() here! Instead exit the loop */
        if (res != GENAPI_E_OK)
        {
            break;
        }

        /* Let the camera acquire images. */
        res = PylonDeviceExecuteCommandFeature( hDev[deviceIndex], "AcquisitionStart" );
        /* do not call CHECK() here! Instead exit the loop */
        if (res != GENAPI_E_OK)
        {
            break;
        }
    }


    /* Only start the grab loop if all cameras have been "started" */
    if (res == GENAPI_E_OK)
    {
        unsigned int nGrabs = 0;

        /* Set the timer to 5 s and start it. */
#ifdef GENAPIC_WIN_BUILD
        LARGE_INTEGER intv;

        intv.QuadPart = -50000000I64;
        if (!SetWaitableTimer( hTimer, &intv, 0, NULL, NULL, FALSE ))
        {
            fprintf( stderr, "SetWaitableTimer() failed.\n" );
            PylonTerminate();
            pressEnterToExit();
            exit( EXIT_FAILURE );
        }
#else
        struct itimerspec timer_value;

        timer_value.it_interval.tv_sec = 0;
        timer_value.it_interval.tv_nsec = 0;
        timer_value.it_value.tv_sec = 5;
        timer_value.it_value.tv_nsec = 0;
        if (timerfd_settime( fdTimer, 0, &timer_value, NULL ) == -1)
        {
            fprintf( stderr, "timerfd_settime() failed. %s\n", strerror( errno ) );
            PylonTerminate();
            pressEnterToExit();
            exit( EXIT_FAILURE );
        }
#endif

        /* Grab until the timer expires. */
        for (;;)
        {
            _Bool isReady;
            size_t woidx;
            unsigned char min, max;
            PylonGrabResult_t grabResult;

            /* Wait for the next buffer to be filled. Wait up to 1000 ms. */
            res = PylonWaitObjectsWaitForAny( wos, 1000, &woidx, &isReady );
            CHECK( res );
            if (!isReady)
            {
                /* Timeout occurred. */
                fputs( "Grab timeout occurred.\n", stderr );
                break; /* Stop grabbing. */
            }

            /* If the timer has expired, exit the grab loop */
            if (woidx == 0)
            {
                fputs( "Grabbing completed successfully.\n", stderr );
                break;  /* timer expired */
            }

            /* Account for the timer. */
            --woidx;

            /* Retrieve the grab result. */
            res = PylonStreamGrabberRetrieveResult( hGrabber[woidx], &grabResult, &isReady );
            CHECK( res );
            if (!isReady)
            {
                /* Oops. No grab result available? We should never have reached this point.
                   Since the wait operation above returned without a timeout, a grab result
                   should be available. */
                fprintf( stderr, "Failed to retrieve a grab result\n" );
                break;
            }

            /* Check to see if the image was grabbed successfully. */
            if (grabResult.Status == Grabbed)
            {
                /* Success. Perform image processing. Since we passed more than one buffer
                   to the stream grabber, the remaining buffers are filled while
                   we do the image processing. The processed buffer won't be touched by
                   the stream grabber until we pass it back to the stream grabber. */

                /* Pointer to the buffer attached to the grab result
                   Get the buffer pointer from the result structure. Since we also got the buffer index,
                   we could alternatively use buffers[bufferIndex]. */
                unsigned char* buffer = (unsigned char*) grabResult.pBuffer;

                /* Perform processing. */
                getMinMax( buffer, grabResult.SizeX, grabResult.SizeY, &min, &max );
                printf( "Grabbed frame #%2u from camera %2u into buffer %2p. Min. val=%3u, Max. val=%3u\n",
                        nGrabs, (unsigned int) woidx, grabResult.Context, min, max );

#ifdef GENAPIC_WIN_BUILD
                /* Display image */
                res = PylonImageWindowDisplayImageGrabResult( woidx, &grabResult );
                CHECK( res );
#endif
            }
            else if (grabResult.Status == Failed)
            {
                fprintf( stderr, "Frame %u wasn't grabbed successfully.  Error code = 0x%08X\n",
                         nGrabs, grabResult.ErrorCode );
            }

            /* Once finished with the processing, requeue the buffer to be filled again. */
            res = PylonStreamGrabberQueueBuffer( hGrabber[woidx], grabResult.hBuffer, grabResult.Context );
            CHECK( res );

            nGrabs++;
        }
    }


    /* Clean up. */

    /* Stop the image acquisition on the cameras. */
    for (deviceIndex = 0; deviceIndex < NUM_DEVICES; ++deviceIndex)
    {
        /* ... Stop the camera. */
        res = PylonDeviceExecuteCommandFeature( hDev[deviceIndex], "AcquisitionStop" );
        CHECK( res );

        /* ... Stop the image acquisition engine. */
        res = PylonStreamGrabberStopStreamingIfMandatory( hGrabber[deviceIndex] );
        CHECK( res );
    }

    /* Remove all wait objects from waitobjects. */
    res = PylonWaitObjectsRemoveAll( wos );
    CHECK( res );
    res = PylonWaitObjectDestroy( woTimer );
    CHECK( res );
    res = PylonWaitObjectsDestroy( wos );
    CHECK( res );


    for (deviceIndex = 0; deviceIndex < NUM_DEVICES; ++deviceIndex)
    {
        size_t i;
        _Bool rdy;
        PylonGrabResult_t grabResult;

        /* ... We must issue a flush call to ensure that all pending buffers are put into the
           stream grabber's output queue. */
        res = PylonStreamGrabberFlushBuffersToOutput( hGrabber[deviceIndex] );
        CHECK( res );

        /* ... The buffers can now be retrieved from the stream grabber. */
        do
        {
            res = PylonStreamGrabberRetrieveResult( hGrabber[deviceIndex], &grabResult, &rdy );
            CHECK( res );
        } while (rdy);

        /* ... When all buffers are retrieved from the stream grabber, they can be deregistered.
           After deregistering the buffers, it is safe to free the memory. */

        for (i = 0; i < NUM_BUFFERS; ++i)
        {
            res = PylonStreamGrabberDeregisterBuffer( hGrabber[deviceIndex], bufHandles[deviceIndex][i] );
            CHECK( res );
            free( buffers[deviceIndex][i] );
        }

        /* ... Release grabbing related resources. */
        res = PylonStreamGrabberFinishGrab( hGrabber[deviceIndex] );
        CHECK( res );

        /* After calling PylonStreamGrabberFinishGrab(), parameters that impact the payload size (e.g.,
           the AOI width and height parameters) are unlocked and can be modified again. */

        /* ... Close the stream grabber. */
        res = PylonStreamGrabberClose( hGrabber[deviceIndex] );
        CHECK( res );


        /* ... Close and release the pylon device. The stream grabber becomes invalid
           after closing the pylon device. Don't call stream grabber related methods after
           closing or releasing the device. */
        res = PylonDeviceClose( hDev[deviceIndex] );
        CHECK( res );
        res = PylonDestroyDevice( hDev[deviceIndex] );
        CHECK( res );
    }

    pressEnterToExit();

    /* ... Shut down the pylon runtime system. Don't call any pylon function after
       calling PylonTerminate(). */
    PylonTerminate();

#ifdef GENAPIC_LINUX_BUILD
    close( fdTimer );
#endif
    return EXIT_SUCCESS;
}

/* This method demonstrates how to retrieve the error message for the last failed
   function call. */
void printErrorAndExit( GENAPIC_RESULT errc )
{
    char* errMsg;
    size_t length;

    /* Retrieve the error message.
       ... First find out how big the buffer must be, */
    GenApiGetLastErrorMessage( NULL, &length );
    errMsg = (char*) alloca( length );
    /* ... and retrieve the message. */
    GenApiGetLastErrorMessage( errMsg, &length );

    fprintf( stderr, "%s (%#08x).\n", errMsg, (unsigned int) errc );

    PylonTerminate();  /* Releases all pylon resources. */
    pressEnterToExit();

    exit( EXIT_FAILURE );
}



/* Simple "image processing" function returning the minimum and maximum gray
   value of an 8 bit gray value image. */
void getMinMax( const unsigned char* pImg, int32_t width, int32_t height,
                unsigned char* pMin, unsigned char* pMax )
{
    unsigned char min = 255;
    unsigned char max = 0;
    unsigned char val;
    const unsigned char* p;

    for (p = pImg; p < pImg + width * height; p++)
    {
        val = *p;
        if (val > max)
            max = val;
        if (val < min)
            min = val;
    }
    *pMin = min;
    *pMax = max;
}

/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void )
{
    fprintf( stderr, "\nPress enter to exit.\n" );
    while (getchar() != '\n');
}

OverlappedGrab Sample#

/*
   This sample illustrates how to grab and process images asynchronously, i.e.,
   while the application is processing a buffer, the acquistion of the next buffer is done
   in parallel.
   The sample uses a pool of buffers that are passed to a stream grabber to be filled with
   image data. Once a buffer is filled and ready for processing, the buffer is retrieved from
   the stream grabber, processed, and passed back to the stream grabber to be filled again.
   Buffers retrieved from the stream grabber are not overwritten as long as
   they are not passed back to the stream grabber.
*/


#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>

#include <pylonc/PylonC.h>

#define CHECK( errc ) if ( GENAPI_E_OK != errc ) printErrorAndExit( errc )

/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void );

/* This method demonstrates how to retrieve the error message for the last failed function call. */
void printErrorAndExit( GENAPIC_RESULT errc );

/* Calculating the minimum and maximum gray value */
void getMinMax( const unsigned char* pImg, int32_t width, int32_t height,
                unsigned char* pMin, unsigned char* pMax );

#define NUM_GRABS 100         /* Number of images to grab. */
#define NUM_BUFFERS 5         /* Number of buffers used for grabbing. */

int main( void )
{
    GENAPIC_RESULT              res;                      /* Return value of pylon methods. */
    size_t                      numDevices;               /* Number of available devices. */
    PYLON_DEVICE_HANDLE         hDev;                     /* Handle for the pylon device. */
    PYLON_STREAMGRABBER_HANDLE  hGrabber;                 /* Handle for the pylon stream grabber. */
    PYLON_WAITOBJECT_HANDLE     hWait;                    /* Handle used for waiting for a grab to be finished. */
    size_t                      payloadSize;              /* Size of an image frame in bytes. */
    unsigned char*              buffers[NUM_BUFFERS];     /* Buffers used for grabbing. */
    PYLON_STREAMBUFFER_HANDLE   bufHandles[NUM_BUFFERS];  /* Handles for the buffers. */
    PylonGrabResult_t           grabResult;               /* Stores the result of a grab operation. */
    int                         nGrabs;                   /* Counts the number of buffers grabbed. */
    size_t                      nStreams;                 /* The number of streams the device provides. */
    _Bool                       isAvail;                  /* Used for checking feature availability. */
    _Bool                       isReady;                  /* Used as an output parameter. */
    size_t                      i;                        /* Counter. */

    /* Before using any pylon methods, the pylon runtime must be initialized. */
    PylonInitialize();

    /* Enumerate all camera devices. You must call
    PylonEnumerateDevices() before creating a device. */
    res = PylonEnumerateDevices( &numDevices );
    CHECK( res );
    if (0 == numDevices)
    {
        fprintf( stderr, "No devices found.\n" );
        PylonTerminate();
        pressEnterToExit();
        exit( EXIT_FAILURE );
    }

    /* Get a handle for the first device found.  */
    res = PylonCreateDeviceByIndex( 0, &hDev );
    CHECK( res );

    /* Before using the device, it must be opened. Open it for configuring
    parameters and for grabbing images. */
    res = PylonDeviceOpen( hDev, PYLONC_ACCESS_MODE_CONTROL | PYLONC_ACCESS_MODE_STREAM );
    CHECK( res );

    /* Print out the name of the camera we are using. */
    {
        char buf[256];
        size_t siz = sizeof( buf );
        _Bool isReadable;

        isReadable = PylonDeviceFeatureIsReadable( hDev, "DeviceModelName" );
        if (isReadable)
        {
            res = PylonDeviceFeatureToString( hDev, "DeviceModelName", buf, &siz );
            CHECK( res );
            printf( "Using camera %s\n", buf );
        }
    }

    /* Set the pixel format to Mono8 if available, where gray values will be output as 8 bit values for each pixel. */
    isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_PixelFormat_Mono8" );
    if (isAvail)
    {
        res = PylonDeviceFeatureFromString( hDev, "PixelFormat", "Mono8" );
        CHECK( res );
    }

    /* Disable acquisition start trigger if available. */
    isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_TriggerSelector_AcquisitionStart" );
    if (isAvail)
    {
        res = PylonDeviceFeatureFromString( hDev, "TriggerSelector", "AcquisitionStart" );
        CHECK( res );
        res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "Off" );
        CHECK( res );
    }

    /* Disable frame burst start trigger if available. */
    isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_TriggerSelector_FrameBurstStart" );
    if (isAvail)
    {
        res = PylonDeviceFeatureFromString( hDev, "TriggerSelector", "FrameBurstStart" );
        CHECK( res );
        res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "Off" );
        CHECK( res );
    }

    /* Disable frame start trigger if available. */
    isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_TriggerSelector_FrameStart" );
    if (isAvail)
    {
        res = PylonDeviceFeatureFromString( hDev, "TriggerSelector", "FrameStart" );
        CHECK( res );
        res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "Off" );
        CHECK( res );
    }

    /* We will use the Continuous frame acquisition mode, i.e., the camera delivers
    images continuously. */
    res = PylonDeviceFeatureFromString( hDev, "AcquisitionMode", "Continuous" );
    CHECK( res );


    /* For GigE cameras, we recommend increasing the packet size for better
       performance. When the network adapter supports jumbo frames, set the packet
       size to a value > 1500, e.g., to 8192. In this sample, we only set the packet size
       to 1500. */
    /* ... Check first to see if the GigE camera packet size parameter is supported and if it is writable. */
    isAvail = PylonDeviceFeatureIsWritable( hDev, "GevSCPSPacketSize" );
    if (isAvail)
    {
        /* ... The device supports the packet size feature, set a value. */
        res = PylonDeviceSetIntegerFeature( hDev, "GevSCPSPacketSize", 1500 );
        CHECK( res );
    }


    /* Image grabbing is done using a stream grabber.
      A device may be able to provide different streams. A separate stream grabber must
      be used for each stream. In this sample, we create a stream grabber for the default
      stream, i.e., the first stream ( index == 0 ).
      */

    /* Get the number of streams supported by the device and the transport layer. */
    res = PylonDeviceGetNumStreamGrabberChannels( hDev, &nStreams );
    CHECK( res );
    if (nStreams < 1)
    {
        fprintf( stderr, "The transport layer doesn't support image streams\n" );
        PylonTerminate();
        pressEnterToExit();
        exit( EXIT_FAILURE );
    }

    /* Create and open a stream grabber for the first channel. */
    res = PylonDeviceGetStreamGrabber( hDev, 0, &hGrabber );
    CHECK( res );
    res = PylonStreamGrabberOpen( hGrabber );
    CHECK( res );

    /* Get a handle for the stream grabber's wait object. The wait object
       allows waiting for buffers to be filled with grabbed data. */
    res = PylonStreamGrabberGetWaitObject( hGrabber, &hWait );
    CHECK( res );


    /* Determine the minimum size of the grab buffer.
       The size is determined by the configuration of the camera
       and the stream grabber. Be aware that this may change
       by changing critical parameters after this call.*/
    res = PylonStreamGrabberGetPayloadSize( hDev, hGrabber, &payloadSize );
    CHECK( res );

    /* Allocate memory for grabbing.  */
    for (i = 0; i < NUM_BUFFERS; ++i)
    {
        buffers[i] = (unsigned char*) malloc( payloadSize );
        if (NULL == buffers[i])
        {
            fprintf( stderr, "Out of memory!\n" );
            PylonTerminate();
            pressEnterToExit();
            exit( EXIT_FAILURE );
        }
    }

    /* We must tell the stream grabber the number and size of the buffers
        we are using. */
    /* .. We will not use more than NUM_BUFFERS for grabbing. */
    res = PylonStreamGrabberSetMaxNumBuffer( hGrabber, NUM_BUFFERS );
    CHECK( res );
    /* .. We will not use buffers bigger than payloadSize bytes. */
    res = PylonStreamGrabberSetMaxBufferSize( hGrabber, payloadSize );
    CHECK( res );


    /*  Allocate the resources required for grabbing. After this, critical parameters
        that impact the payload size must not be changed until FinishGrab() is called. */
    res = PylonStreamGrabberPrepareGrab( hGrabber );
    CHECK( res );


    /* Before using the buffers for grabbing, they must be registered at
       the stream grabber. For each registered buffer, a buffer handle
       is returned. After registering, these handles are used instead of the
       raw pointers. */
    for (i = 0; i < NUM_BUFFERS; ++i)
    {
        res = PylonStreamGrabberRegisterBuffer( hGrabber, buffers[i], payloadSize, &bufHandles[i] );
        CHECK( res );
    }

    /* Feed the buffers into the stream grabber's input queue. For each buffer, the API
       allows passing in a pointer to additional context information. This pointer
       will be returned unchanged when the grab is finished. In our example, we use the index of the
       buffer as context information. */
    for (i = 0; i < NUM_BUFFERS; ++i)
    {
        res = PylonStreamGrabberQueueBuffer( hGrabber, bufHandles[i], (void*) i );
        CHECK( res );
    }

    /* Now the stream grabber is prepared. As soon as the camera starts to acquire images,
       the image data will be grabbed into the buffers provided.  */

    /* Start the image acquisition engine. */
    res = PylonStreamGrabberStartStreamingIfMandatory( hGrabber );
    CHECK( res );

    /* Let the camera acquire images. */
    res = PylonDeviceExecuteCommandFeature( hDev, "AcquisitionStart" );
    CHECK( res );

    /* Grab NUM_GRABS images */
    nGrabs = 0;                         /* Counts the number of images grabbed */
    while (nGrabs < NUM_GRABS)
    {
        size_t bufferIndex;              /* Index of the buffer */
        unsigned char min, max;
        /* Wait for the next buffer to be filled. Wait up to 1000 ms. */
        res = PylonWaitObjectWait( hWait, 1000, &isReady );
        CHECK( res );
        if (!isReady)
        {
            /* Timeout occurred. */
            fprintf( stderr, "Grab timeout occurred\n" );
            break; /* Stop grabbing. */
        }

        /* Since the wait operation was successful, the result of at least one grab
           operation is available. Retrieve it. */
        res = PylonStreamGrabberRetrieveResult( hGrabber, &grabResult, &isReady );
        CHECK( res );
        if (!isReady)
        {
            /* Oops. No grab result available? We should never have reached this point.
               Since the wait operation above returned without a timeout, a grab result
               should be available. */
            fprintf( stderr, "Failed to retrieve a grab result\n" );
            break;
        }

        nGrabs++;

        /* Get the buffer index from the context information. */
        bufferIndex = (size_t) grabResult.Context;

        /* Check to see if the image was grabbed successfully. */
        if (grabResult.Status == Grabbed)
        {
            /*  Success. Perform image processing. Since we passed more than one buffer
            to the stream grabber, the remaining buffers are filled while
            we do the image processing. The processed buffer won't be touched by
            the stream grabber until we pass it back to the stream grabber. */

            unsigned char* buffer;        /* Pointer to the buffer attached to the grab result. */

            /* Get the buffer pointer from the result structure. Since we also got the buffer index,
               we could alternatively use buffers[bufferIndex]. */
            buffer = (unsigned char*) grabResult.pBuffer;

            /* Perform processing. */
            getMinMax( buffer, grabResult.SizeX, grabResult.SizeY, &min, &max );
            printf( "Grabbed frame %2d into buffer %2d. Min. gray value = %3u, Max. gray value = %3u\n",
                    nGrabs, (int) bufferIndex, min, max );

#ifdef GENAPIC_WIN_BUILD
            /* Display image */
            res = PylonImageWindowDisplayImageGrabResult( 0, &grabResult );
            CHECK( res );
#endif

        }
        else if (grabResult.Status == Failed)
        {
            fprintf( stderr, "Frame %d wasn't grabbed successfully.  Error code = 0x%08X\n",
                     nGrabs, grabResult.ErrorCode );
        }

        /* Once finished with the processing, requeue the buffer to be filled again. */
        res = PylonStreamGrabberQueueBuffer( hGrabber, grabResult.hBuffer, (void*) bufferIndex );
        CHECK( res );
    }

    /* Clean up. */

    /*  ... Stop the camera. */
    res = PylonDeviceExecuteCommandFeature( hDev, "AcquisitionStop" );
    CHECK( res );

    /* ... Stop the image acquisition engine. */
    res = PylonStreamGrabberStopStreamingIfMandatory( hGrabber );
    CHECK( res );

    /* ... We must issue a flush call to ensure that all pending buffers are put into the
       stream grabber's output queue. */
    res = PylonStreamGrabberFlushBuffersToOutput( hGrabber );
    CHECK( res );

    /* ... The buffers can now be retrieved from the stream grabber. */
    do
    {
        res = PylonStreamGrabberRetrieveResult( hGrabber, &grabResult, &isReady );
        CHECK( res );
    } while (isReady);

    /* ... When all buffers have been retrieved from the stream grabber, they can be deregistered.
           After that, it is safe to free the memory. */

    for (i = 0; i < NUM_BUFFERS; ++i)
    {
        res = PylonStreamGrabberDeregisterBuffer( hGrabber, bufHandles[i] );
        CHECK( res );
        free( buffers[i] );
    }

    /* ... Release grabbing related resources. */
    res = PylonStreamGrabberFinishGrab( hGrabber );
    CHECK( res );

    /* After calling PylonStreamGrabberFinishGrab(), parameters that impact the payload size (e.g.,
    the AOI width and height parameters) are unlocked and can be modified again. */

    /* ... Close the stream grabber. */
    res = PylonStreamGrabberClose( hGrabber );
    CHECK( res );


    /* ... Close and release the pylon device. The stream grabber becomes invalid
       after closing the pylon device. Don't call stream grabber related methods after
       closing or releasing the device. */
    res = PylonDeviceClose( hDev );
    CHECK( res );

    /* ...The device is no longer used, destroy it. */
    res = PylonDestroyDevice( hDev );
    CHECK( res );

    pressEnterToExit();

    /* ... Shut down the pylon runtime system. Don't call any pylon method after
       calling PylonTerminate(). */
    PylonTerminate();


    return EXIT_SUCCESS;
}

/* This function demonstrates how to retrieve the error message for the last failed
   function call. */
void printErrorAndExit( GENAPIC_RESULT errc )
{
    char* errMsg;
    size_t length;

    /* Retrieve the error message.
    ... First find out how big the buffer must be, */
    GenApiGetLastErrorMessage( NULL, &length );
    errMsg = (char*) malloc( length );
    /* ... and retrieve the message. */
    GenApiGetLastErrorMessage( errMsg, &length );

    fprintf( stderr, "%s (%#08x).\n", errMsg, (unsigned int) errc );
    free( errMsg );

    /* Retrieve more details about the error.
    ... First find out how big the buffer must be, */
    GenApiGetLastErrorDetail( NULL, &length );
    errMsg = (char*) malloc( length );
    /* ... and retrieve the message. */
    GenApiGetLastErrorDetail( errMsg, &length );

    fprintf( stderr, "%s\n", errMsg );
    free( errMsg );

    PylonTerminate();  /* Releases all pylon resources */
    pressEnterToExit();

    exit( EXIT_FAILURE );
}



/* Simple "image processing" function returning the minimum and maximum gray
   value of an 8 bit gray value image. */
void getMinMax( const unsigned char* pImg, int32_t width, int32_t height,
                unsigned char* pMin, unsigned char* pMax )
{
    unsigned char min = 255;
    unsigned char max = 0;
    unsigned char val;
    const unsigned char* p;

    for (p = pImg; p < pImg + width * height; p++)
    {
        val = *p;
        if (val > max)
            max = val;
        if (val < min)
            min = val;
    }
    *pMin = min;
    *pMax = max;
}

/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void )
{
    fprintf( stderr, "\nPress enter to exit.\n" );
    while (getchar() != '\n');
}

ParametrizeCamera Sample#

/*
  This sample illustrates how to read and write the different camera
  parameter types.
*/

#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#ifdef __GNUC__
#   include <alloca.h>
#endif

#include <pylonc/PylonC.h>

#define CHECK( errc ) if ( GENAPI_E_OK != errc ) printErrorAndExit( errc )

/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void );

void printErrorAndExit( GENAPIC_RESULT errc );
void demonstrateAccessibilityCheck( PYLON_DEVICE_HANDLE );
void demonstrateIntFeature( PYLON_DEVICE_HANDLE );
void demonstrateInt32Feature( PYLON_DEVICE_HANDLE );
void demonstrateFloatFeature( PYLON_DEVICE_HANDLE );
void demonstrateBooleanFeature( PYLON_DEVICE_HANDLE );
void demonstrateFromStringToString( PYLON_DEVICE_HANDLE );
void demonstrateEnumFeature( PYLON_DEVICE_HANDLE );
void demonstrateCommandFeature( PYLON_DEVICE_HANDLE );


int main( void )
{
    GENAPIC_RESULT              res;           /* Return value of pylon methods. */
    size_t                      numDevices;    /* Number of available devices. */
    PYLON_DEVICE_HANDLE         hDev;          /* Handle for the pylon device. */

    /* Before using any pylon methods, the pylon runtime must be initialized. */
    PylonInitialize();

    /* Enumerate all camera devices. You must call
    PylonEnumerateDevices() before creating a device. */
    res = PylonEnumerateDevices( &numDevices );
    CHECK( res );
    if (0 == numDevices)
    {
        fprintf( stderr, "No devices found.\n" );
        PylonTerminate();
        pressEnterToExit();
        exit( EXIT_FAILURE );
    }

    /* Get a handle for the first device found.  */
    res = PylonCreateDeviceByIndex( 0, &hDev );
    CHECK( res );

    /* Before using the device, it must be opened. Open it for configuring
    parameters and for grabbing images. */
    res = PylonDeviceOpen( hDev, PYLONC_ACCESS_MODE_CONTROL | PYLONC_ACCESS_MODE_STREAM );
    CHECK( res );

    /* Print out the name of the camera we are using. */
    {
        char buf[256];
        size_t siz = sizeof( buf );
        _Bool isReadable;

        isReadable = PylonDeviceFeatureIsReadable( hDev, "DeviceModelName" );
        if (isReadable)
        {
            res = PylonDeviceFeatureToString( hDev, "DeviceModelName", buf, &siz );
            CHECK( res );
            printf( "Using camera %s\n", buf );
        }
    }

    /* Demonstrate how to check the accessibility of a feature. */
    demonstrateAccessibilityCheck( hDev );
    puts( "" );

    /* Demonstrate how to handle integer camera parameters. */
    demonstrateIntFeature( hDev );
    puts( "" );
    demonstrateInt32Feature( hDev );
    puts( "" );

    /* Demonstrate how to handle floating point camera parameters. */
    demonstrateFloatFeature( hDev );
    puts( "" );

    /* Demonstrate how to handle boolean camera parameters. */
    demonstrateBooleanFeature( hDev );
    puts( "" );

    /* Each feature can be read as a string and also set as a string. */
    demonstrateFromStringToString( hDev );
    puts( "" );

    /* Demonstrate how to handle enumeration camera parameters. */
    demonstrateEnumFeature( hDev );
    puts( "" );

    /* Demonstrate how to execute actions. */
    demonstrateCommandFeature( hDev );



    /* Clean up. Close and release the pylon device. */

    res = PylonDeviceClose( hDev );
    CHECK( res );
    res = PylonDestroyDevice( hDev );
    CHECK( res );


    /* Shut down the pylon runtime system. Don't call any pylon method after
       calling PylonTerminate(). */
    PylonTerminate();
    pressEnterToExit();

    return EXIT_SUCCESS;
}

/* This function demonstrates how to retrieve the error message for the last failed
   function call. */
void printErrorAndExit( GENAPIC_RESULT errc )
{
    char* errMsg;
    size_t length;

    /* Retrieve the error message.
    ... First find out how big the buffer must be, */
    GenApiGetLastErrorMessage( NULL, &length );
    errMsg = (char*) malloc( length );
    /* ... and retrieve the message. */
    GenApiGetLastErrorMessage( errMsg, &length );

    fprintf( stderr, "%s (%#08x).\n", errMsg, (unsigned int) errc );
    free( errMsg );

    /* Retrieve the more details about the error
    ... First find out how big the buffer must be, */
    GenApiGetLastErrorDetail( NULL, &length );
    errMsg = (char*) malloc( length );
    /* ... and retrieve the message. */
    GenApiGetLastErrorDetail( errMsg, &length );

    fprintf( stderr, "%s\n", errMsg );
    free( errMsg );

    PylonTerminate();  /* Releases all pylon resources */
    pressEnterToExit();

    exit( EXIT_FAILURE );
}

/* This function demonstrates how to check the presence, readability, and writability
   of a feature. */
void demonstrateAccessibilityCheck( PYLON_DEVICE_HANDLE hDev )
{
    _Bool val;  /* Output of the check functions */

    /* Check to see if a feature is implemented at all. */
    val = PylonDeviceFeatureIsImplemented( hDev, "Width" );
    printf( "The 'Width' feature %s implemented\n", val ? "is" : "isn't" );
    val = PylonDeviceFeatureIsImplemented( hDev, "MyCustomFeature" );
    printf( "The 'MyCustomFeature' feature %s implemented\n", val ? "is" : "isn't" );


    /* Although a feature is implemented by the device, it might not be available
       with the device in its current state. Check to see if the feature is currently
       available. The PylonDeviceFeatureIsAvailable sets val to 0 if either the feature
       is not implemented or if the feature is not currently available. */

    val = PylonDeviceFeatureIsAvailable( hDev, "BinningVertical" );
    printf( "The 'BinningVertical' feature %s available\n", val ? "is" : "isn't" );

    /* If a feature is available, it could be read-only, write-only, or both
       readable and writable. Use the PylonDeviceFeatureIsReadable() and the
       PylonDeviceFeatureIsWritable() functions(). It is safe to call these functions
       for features that are currently not available or not implemented by the device.
       A feature that is not available or not implemented is neither readable nor writable.
       The readability and writability of a feature can change depending on the current
       state of the device. For example, the Width parameter might not be writable when
       the camera is acquiring images. */


    val = PylonDeviceFeatureIsReadable( hDev, "Width" );
    printf( "The 'Width' feature %s readable\n", val ? "is" : "isn't" );
    val = PylonDeviceFeatureIsReadable( hDev, "MyCustomFeature" );
    printf( "The 'MyCustomFeature' feature %s readable\n", val ? "is" : "isn't" );

    val = PylonDeviceFeatureIsWritable( hDev, "Width" );
    printf( "The 'Width' feature %s writable\n", val ? "is" : "isn't" );

    printf( "\n" );
}


/* This function demonstrates how to handle integer camera parameters. */
void demonstrateIntFeature( PYLON_DEVICE_HANDLE hDev )
{
    static const char   featureName[] = "Width";  /* Name of the feature used in this sample: AOI Width */
    int64_t             val, min, max, incr;      /* Properties of the feature */
    GENAPIC_RESULT      res;                      /* Return value */


    if (PylonDeviceFeatureIsReadable( hDev, featureName ))
    {
        /*
          Query the current value, the allowed value range, and the increment of the feature.
          For some integer features, you are not allowed to set every value within the
          value range. For example, for some cameras the Width parameter must be a multiple
          of 2. These constraints are expressed by the increment value. Valid values
          follow the rule: val >= min && val <= max && val == min + n * inc. */
        res = PylonDeviceGetIntegerFeatureMin( hDev, featureName, &min );  /* Get the minimum value. */
        CHECK( res );
        res = PylonDeviceGetIntegerFeatureMax( hDev, featureName, &max );  /* Get the maximum value. */
        CHECK( res );
        res = PylonDeviceGetIntegerFeatureInc( hDev, featureName, &incr );  /* Get the increment value. */
        CHECK( res );
        res = PylonDeviceGetIntegerFeature( hDev, featureName, &val );     /* Get the current value. */
        CHECK( res );

#if __STDC_VERSION__ >= 199901L || defined(__GNUC__)
        printf( "%s: min= %lld  max= %lld  incr=%lld  Value=%lld\n", featureName, (long long) min, (long long) max, (long long) incr, (long long) val );
#else
        printf( "%s: min= %I64d  max= %I64d  incr=%I64d  Value=%I64d\n", featureName, min, max, incr, val );
#endif

        if (PylonDeviceFeatureIsWritable( hDev, featureName ))
        {
            /* Set the Width half-way between minimum and maximum. */
            res = PylonDeviceSetIntegerFeature( hDev, featureName, min + (max - min) / incr / 2 * incr );
            CHECK( res );
        }
        else
            fprintf( stderr, "The %s feature is not writable.\n", featureName );
    }
    else
        fprintf( stderr, "The %s feature is not readable.\n", featureName );
}


/* The integer functions illustrated above take 64 bit integers as output parameters. There are variants
   of the integer functions that accept 32 bit integers instead. The Get.... functions return
   an error when the value returned by the device doesn't fit into a 32 bit integer. */
void demonstrateInt32Feature( PYLON_DEVICE_HANDLE hDev )
{
    static const char   featureName[] = "Height";  /* Name of the feature used in this sample: AOI height */
    int32_t             val, min, max, incr;       /* Properties of the feature */
    GENAPIC_RESULT      res;                       /* Return value */


    if (PylonDeviceFeatureIsReadable( hDev, featureName ))
    {
        /*
           Query the current value, the allowed value range, and the increment of the feature.
           For some integer features, you are not allowed to set every value within the
           value range. For example, for some cameras the Width parameter must be a multiple
           of 2. These constraints are expressed by the increment value. Valid values
           follow the rule: val >= min && val <= max && val == min + n * inc. */
        res = PylonDeviceGetIntegerFeatureMinInt32( hDev, featureName, &min );  /* Get the minimum value. */
        CHECK( res );
        res = PylonDeviceGetIntegerFeatureMaxInt32( hDev, featureName, &max );  /* Get the maximum value. */
        CHECK( res );
        res = PylonDeviceGetIntegerFeatureIncInt32( hDev, featureName, &incr );  /* Get the increment value. */
        CHECK( res );
        res = PylonDeviceGetIntegerFeatureInt32( hDev, featureName, &val );     /* Get the current value. */
        CHECK( res );
        printf( "%s: min= %d  max= %d  incr=%d  Value=%d\n", featureName, min, max, incr, val );

        if (PylonDeviceFeatureIsWritable( hDev, featureName ))
        {
            /* Set the value to half its maximum  */
            res = PylonDeviceSetIntegerFeatureInt32( hDev, featureName, min + (max - min) / incr / 2 * incr );
            CHECK( res );
        }
        else
            fprintf( stderr, "The %s feature is not writable.\n", featureName );
    }
    else
        fprintf( stderr, "The %s feature is not readable.\n", featureName );
}


/* Some features are floating point features. This function illustrates how to set and get floating
   point parameters. */
void demonstrateFloatFeature( PYLON_DEVICE_HANDLE hDev )
{
    static const char   featureName[] = "Gamma";  /* The name of the feature used */
    _Bool                isWritable;               /* Is the feature writable? */
    double              min, max, value;          /* Value range and current value */
    GENAPIC_RESULT      res;                      /* Return value */

    if (PylonDeviceFeatureIsReadable( hDev, featureName ))
    {
        /* Query the value range and the current value. */
        res = PylonDeviceGetFloatFeatureMin( hDev, featureName, &min );
        CHECK( res );
        res = PylonDeviceGetFloatFeatureMax( hDev, featureName, &max );
        CHECK( res );
        res = PylonDeviceGetFloatFeature( hDev, featureName, &value );
        CHECK( res );

        printf( "%s: min = %4.2f, max = %4.2f, value = %4.2f\n", featureName, min, max, value );

        /* Set the value to half its maximum. */
        isWritable = PylonDeviceFeatureIsWritable( hDev, featureName );
        if (isWritable)
        {
            value = 0.5 * (min + max);
            printf( "Setting %s to %4.2f\n", featureName, value );
            res = PylonDeviceSetFloatFeature( hDev, featureName, value );
            CHECK( res );
        }
        else
            fprintf( stderr, "The %s feature is not writable.\n", featureName );
    }
    else
        fprintf( stderr, "The %s feature is not readable.\n", featureName );
}


/* Some features are boolean features that can be switched on and off.
   This function illustrates how to access boolean features. */
void demonstrateBooleanFeature( PYLON_DEVICE_HANDLE hDev )
{
    static const char   featureName[] = "GammaEnable"; /* The name of the feature */
    _Bool                isWritable;                    /* Is the feature writable? */
    _Bool                value;                         /* The value of the feature */
    GENAPIC_RESULT      res;                           /* Return value */

    /* Check to see if the feature is writable. */
    isWritable = PylonDeviceFeatureIsWritable( hDev, featureName );

    if (isWritable)
    {
        /* Retrieve the current state of the feature. */
        res = PylonDeviceGetBooleanFeature( hDev, featureName, &value );
        CHECK( res );
        printf( "The %s features is %s\n", featureName, value ? "on" : "off" );

        /* Set a new value. */
        value = (_Bool) !value;  /* New value */
        printf( "Switching the %s feature %s\n", featureName, value ? "on" : "off" );
        res = PylonDeviceSetBooleanFeature( hDev, featureName, value );
        CHECK( res );

    }
    else
        printf( "The %s feature isn't writable\n", featureName );
}


/*
  Regardless of the parameter's type, any parameter value can be retrieved as a string. Each parameter
  can be set by passing in a string correspondingly. This function illustrates how to set and get the
  Width parameter as string. As demonstrated above, the Width parameter is of the integer type.
  */
void demonstrateFromStringToString( PYLON_DEVICE_HANDLE hDev )
{
    static const char   featureName[] = "Width";   /* The name of the feature */

    size_t              len;
    char* buf;
    char                smallBuf[1];
    char                properBuf[32];
    GENAPIC_RESULT      res;                       /* Return value */

    /* Get the value of a feature as a string. Normally getting the value consits of 3 steps:
       1.) Determine the required buffer size.
       2.) Allocate the buffer.
       3.) Retrieve the value. */
    /* ... Get the required buffer size. The size is queried by
           passing a NULL pointer as a pointer to the buffer. */
    res = PylonDeviceFeatureToString( hDev, featureName, NULL, &len );
    CHECK( res );
    /* ... Len is set to the required buffer size (terminating zero included).
           Allocate the memory and retrieve the string. */
    buf = (char*) alloca( len );
    res = PylonDeviceFeatureToString( hDev, featureName, buf, &len );
    CHECK( res );

    printf( "%s: %s\n", featureName, buf );

    /* You are not necessarily required to query the buffer size in advance. If the buffer is
       big enough, passing in a buffer and a pointer to its length will work.
       When the buffer is too small, an error is returned. */

    /* Passing in a buffer that is too small */
    len = sizeof( smallBuf );
    res = PylonDeviceFeatureToString( hDev, featureName, smallBuf, &len );
    if (res == GENAPI_E_INSUFFICIENT_BUFFER)
    {
        /* The buffer was too small. The required size is indicated by len. */
        printf( "Buffer is too small for the value of '%s'. The required buffer size is %d\n", featureName, (int) len );
    }
    else
        CHECK( res );  /* Unexpected return value */

    /* Passing in a buffer with sufficient size. */
    len = sizeof( properBuf );
    res = PylonDeviceFeatureToString( hDev, featureName, properBuf, &len );
    CHECK( res );


    /* A feature can be set as a string using the PylonDeviceFeatureFromString() function.
       If the content of a string can not be converted to the type of the feature, an
       error is returned. */
    res = PylonDeviceFeatureFromString( hDev, featureName, "fourty-two" ); /* Can not be converted to an integer */
    if (res != GENAPI_E_OK)
    {
        /* Print out an error message. */
        size_t l;
        char* msg;
        GenApiGetLastErrorMessage( NULL, &l ); /* Retrieve buffer size for the error message */
        msg = (char*) malloc( l );             /* Provide memory */
        GenApiGetLastErrorMessage( msg, &l );  /* Retrieve the message */
        printf( "%s\n", msg );
        free( msg );
    }
}


/* There are camera features that behave like enumerations. These features can take a value from a fixed
   set of possible values. One example is the pixel format feature. This function illustrates how to deal with
   enumeration features.

*/
void demonstrateEnumFeature( PYLON_DEVICE_HANDLE hDev )
{
    char                value[64];                     /* The current value of the feature */
    size_t              len;                           /* The length of the string */
    GENAPIC_RESULT      res;                           /* Return value */
    _Bool               isWritable;
    _Bool               supportsMono8;
    _Bool               supportsYUV422Packed;
    _Bool               supportsMono16;


/* The allowed values for an enumeration feature are represented as strings. Use the
PylonDeviceFeatureFromString() and PylonDeviceFeatureToString() methods for setting and getting
the value of an enumeration feature. */


/* Get the current value of the enumeration feature. */
    len = sizeof( value );
    res = PylonDeviceFeatureToString( hDev, "PixelFormat", value, &len );
    CHECK( res );

    printf( "PixelFormat: %s\n", value );

    /*
      For an enumeration feature, the pylon Viewer's "Feature Documentation" window lists the the
      names of the possible values. Some of the values might not be supported by the device.
      To check if a certain "SomeValue" value for a "SomeFeature" feature can be set, call the
      PylonDeviceFeatureIsAvailable() function with "EnumEntry_SomeFeature_SomeValue" as an argument.
    */
    /* Check to see if the Mono8 pixel format can be set. */
    supportsMono8 = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_PixelFormat_Mono8" );
    printf( "Mono8 %s a supported value for the PixelFormat feature\n", supportsMono8 ? "is" : "isn't" );

    /* Check to see if the YUV422Packed pixel format can be set. */
    supportsYUV422Packed = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_PixelFormat_YUV422Packed" );
    printf( "YUV422Packed %s a supported value for the PixelFormat feature\n", supportsYUV422Packed ? "is" : "isn't" );

    /* Check to see if the Mono16 pixel format can be set. */
    supportsMono16 = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_PixelFormat_Mono16" );
    printf( "Mono16 %s a supported value for the PixelFormat feature\n", supportsMono16 ? "is" : "isn't" );


    /* Before writing a value, we recommend checking to see if the enumeration feature is
       currently writable. */
    isWritable = PylonDeviceFeatureIsWritable( hDev, "PixelFormat" );
    if (isWritable)
    {
        /* The PixelFormat feature is writable, set it to one of the supported values. */
        if (supportsMono16)
        {
            printf( "Setting PixelFormat to Mono16\n" );
            res = PylonDeviceFeatureFromString( hDev, "PixelFormat", "Mono16" );
            CHECK( res );
        }
        else if (supportsYUV422Packed)
        {
            printf( "Setting PixelFormat to YUV422Packed\n" );
            res = PylonDeviceFeatureFromString( hDev, "PixelFormat", "YUV422Packed" );
            CHECK( res );
        }
        else if (supportsMono8)
        {
            printf( "Setting PixelFormat to Mono8\n" );
            res = PylonDeviceFeatureFromString( hDev, "PixelFormat", "Mono8" );
            CHECK( res );
        }

        /* Reset the PixelFormat feature to its previous value. */
        PylonDeviceFeatureFromString( hDev, "PixelFormat", value );
    }

}


/* There are camera features, such as starting image acquisition, that represent a command.
   This function that loads the factory settings, illustrates how to execute a command feature.  */
void demonstrateCommandFeature( PYLON_DEVICE_HANDLE hDev )
{
    GENAPIC_RESULT      res;  /* Return value. */

    /* Before executing the user set load command, the user set selector must be
       set to the default set. Since we are focusing on the command feature,
       we skip the recommended steps for checking the availability of the user set
       related features and values. */

    /* Choose the default configuration set (with one of the factory setups chosen). */
    res = PylonDeviceFeatureFromString( hDev, "UserSetSelector", "Default" );
    CHECK( res );

    /* Execute the user set load command. */
    printf( "Loading the default set.\n" );
    res = PylonDeviceExecuteCommandFeature( hDev, "UserSetLoad" );
    CHECK( res );
}

/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void )
{
    fprintf( stderr, "\nPress enter to exit.\n" );
    while (getchar() != '\n');
}

SimpleGrab Sample#

/*

This sample illustrates how to use the PylonDeviceGrabSingleFrame() convenience
method for grabbing images in a loop. PylonDeviceGrabSingleFrame() grabs one
single frame in single frame mode.

Grabbing in single frame acquisition mode is the easiest way to grab images. Note: in single frame
mode the maximum frame rate of the camera can't be achieved. The full frame
rate can be achieved by setting the camera to the continuous frame acquisition
mode and by grabbing in overlapped mode, i.e., image acquisition is done in parallel
with image processing. This is illustrated in the OverlappedGrab sample program.

*/

#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>

#include <pylonc/PylonC.h>

/* Simple error handling. */
#define CHECK( errc ) if ( GENAPI_E_OK != errc ) printErrorAndExit( errc )

/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void );

/* This method demonstrates how to retrieve the error message
   for the last failed function call. */
void printErrorAndExit( GENAPIC_RESULT errc );

/* Calculating the minimum and maximum gray value of an image buffer */
void getMinMax( const unsigned char* pImg, int32_t width, int32_t height,
                unsigned char* pMin, unsigned char* pMax );


int main( void )
{
    GENAPIC_RESULT          res;           /* Return value of pylon methods. */
    size_t                  numDevices;    /* Number of available devices. */
    PYLON_DEVICE_HANDLE     hDev;          /* Handle for the pylon device. */
    const int               numGrabs = 10; /* Number of images to grab. */
    size_t                  payloadSize = 0; /* Size of an image frame in bytes. */
    unsigned char*          imgBuf;        /* Buffer used for grabbing. */
    _Bool                   isAvail;
    int                     i;

    /* Before using any pylon methods, the pylon runtime must be initialized. */
    PylonInitialize();

    /* Enumerate all camera devices. You must call
    PylonEnumerateDevices() before creating a device! */
    res = PylonEnumerateDevices( &numDevices );
    CHECK( res );
    if (0 == numDevices)
    {
        fprintf( stderr, "No devices found!\n" );
        /* Before exiting a program, PylonTerminate() should be called to release
           all pylon related resources. */
        PylonTerminate();
        pressEnterToExit();
        exit( EXIT_FAILURE );
    }

    /* Get a handle for the first device found.  */
    res = PylonCreateDeviceByIndex( 0, &hDev );
    CHECK( res );

    /* Before using the device, it must be opened. Open it for configuring
    parameters and for grabbing images. */
    res = PylonDeviceOpen( hDev, PYLONC_ACCESS_MODE_CONTROL | PYLONC_ACCESS_MODE_STREAM );
    CHECK( res );

    /* Print out the name of the camera we are using. */
    {
        char buf[256];
        size_t siz = sizeof( buf );
        _Bool isReadable;

        isReadable = PylonDeviceFeatureIsReadable( hDev, "DeviceModelName" );
        if (isReadable)
        {
            res = PylonDeviceFeatureToString( hDev, "DeviceModelName", buf, &siz );
            CHECK( res );
            printf( "Using camera %s\n", buf );
        }
    }

    /* Set the pixel format to Mono8 if available, where gray values will be output as 8 bit values for each pixel. */
    isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_PixelFormat_Mono8" );
    if (isAvail)
    {
        res = PylonDeviceFeatureFromString( hDev, "PixelFormat", "Mono8" );
        CHECK( res );
    }

    /* Disable acquisition start trigger if available */
    isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_TriggerSelector_AcquisitionStart" );
    if (isAvail)
    {
        res = PylonDeviceFeatureFromString( hDev, "TriggerSelector", "AcquisitionStart" );
        CHECK( res );
        res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "Off" );
        CHECK( res );
    }

    /* Disable frame burst start trigger if available. */
    isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_TriggerSelector_FrameBurstStart" );
    if (isAvail)
    {
        res = PylonDeviceFeatureFromString( hDev, "TriggerSelector", "FrameBurstStart" );
        CHECK( res );
        res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "Off" );
        CHECK( res );
    }

    /* Disable frame start trigger if available */
    isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_TriggerSelector_FrameStart" );
    if (isAvail)
    {
        res = PylonDeviceFeatureFromString( hDev, "TriggerSelector", "FrameStart" );
        CHECK( res );
        res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "Off" );
        CHECK( res );
    }

    /* For GigE cameras, we recommend increasing the packet size for better
       performance. If the network adapter supports jumbo frames, set the packet
       size to a value > 1500, e.g., to 8192. In this sample, we only set the packet size
       to 1500. */
    /* ... Check first to see if the GigE camera packet size parameter is supported
        and if it is writable. */
    isAvail = PylonDeviceFeatureIsWritable( hDev, "GevSCPSPacketSize" );
    if (isAvail)
    {
        /* ... The device supports the packet size feature. Set a value. */
        res = PylonDeviceSetIntegerFeature( hDev, "GevSCPSPacketSize", 1500 );
        CHECK( res );
    }

    /* Determine the required size of the grab buffer. */


    {
        PYLON_STREAMGRABBER_HANDLE  hGrabber;
        /* Temporary create and open a stream grabber for the first channel. */
        res = PylonDeviceGetStreamGrabber( hDev, 0, &hGrabber );
        CHECK( res );
        res = PylonStreamGrabberOpen( hGrabber );
        CHECK( res );

        res = PylonStreamGrabberGetPayloadSize( hDev, hGrabber, &payloadSize );
        CHECK( res );

        res = PylonStreamGrabberClose( hGrabber );
        CHECK( res );
    }

    /* Allocate memory for grabbing. */
    imgBuf = (unsigned char*) malloc( payloadSize );
    if (NULL == imgBuf)
    {
        fprintf( stderr, "Out of memory.\n" );
        PylonTerminate();
        pressEnterToExit();
        exit( EXIT_FAILURE );
    }

    /* Grab some images in a loop. */
    for (i = 0; i < numGrabs; ++i)
    {
        unsigned char min, max;
        PylonGrabResult_t grabResult;
        _Bool bufferReady;

        /* Grab one single frame from stream channel 0. The
        camera is set to single frame acquisition mode.
        Wait up to 500 ms for the image to be grabbed. */
        res = PylonDeviceGrabSingleFrame( hDev, 0, imgBuf, payloadSize,
                                          &grabResult, &bufferReady, 500 );
        if (GENAPI_E_OK == res && !bufferReady)
        {
            /* Timeout occurred. */
            printf( "Frame %d: timeout\n", i + 1 );
        }
        CHECK( res );

        /* Check to see if the image was grabbed successfully. */
        if (grabResult.Status == Grabbed)
        {
            /* Success. Perform image processing. */
            getMinMax( imgBuf, grabResult.SizeX, grabResult.SizeY, &min, &max );
            printf( "Grabbed frame #%2d. Min. gray value = %3u, Max. gray value = %3u\n", i + 1, min, max );

#ifdef GENAPIC_WIN_BUILD
            /* Display image */
            res = PylonImageWindowDisplayImageGrabResult( 0, &grabResult );
            CHECK( res );
#endif
        }
        else if (grabResult.Status == Failed)
        {
            fprintf( stderr, "Frame %d wasn't grabbed successfully.  Error code = 0x%08X\n",
                     i + 1, grabResult.ErrorCode );
        }
    }

    /* Clean up. Close and release the pylon device. */

    res = PylonDeviceClose( hDev );
    CHECK( res );
    res = PylonDestroyDevice( hDev );
    CHECK( res );

    /* Free memory for grabbing. */
    free( imgBuf );

    pressEnterToExit();

    /* Shut down the pylon runtime system. Don't call any pylon method after
       calling PylonTerminate(). */
    PylonTerminate();

    return EXIT_SUCCESS;
}

/* This function demonstrates how to retrieve the error message for the last failed
   function call. */
void printErrorAndExit( GENAPIC_RESULT errc )
{
    char* errMsg;
    size_t length;

    /* Retrieve the error message.
    ... Find out first how big the buffer must be, */
    GenApiGetLastErrorMessage( NULL, &length );
    errMsg = (char*) malloc( length );
    /* ... and retrieve the message. */
    GenApiGetLastErrorMessage( errMsg, &length );

    fprintf( stderr, "%s (%#08x).\n", errMsg, (unsigned int) errc );
    free( errMsg );

    /* Retrieve more details about the error.
    ... Find out first how big the buffer must be, */
    GenApiGetLastErrorDetail( NULL, &length );
    errMsg = (char*) malloc( length );
    /* ... and retrieve the message. */
    GenApiGetLastErrorDetail( errMsg, &length );

    fprintf( stderr, "%s\n", errMsg );
    free( errMsg );

    PylonTerminate();  /* Releases all pylon resources. */
    pressEnterToExit();

    exit( EXIT_FAILURE );
}



/* Simple "image processing" function returning the minimum and maximum gray
   value of an 8 bit gray value image. */
void getMinMax( const unsigned char* pImg, int32_t width, int32_t height,
                unsigned char* pMin, unsigned char* pMax )
{
    unsigned char min = 255;
    unsigned char max = 0;
    unsigned char val;
    const unsigned char* p;

    for (p = pImg; p < pImg + width * height; p++)
    {
        val = *p;
        if (val > max)
            max = val;
        if (val < min)
            min = val;
    }
    *pMin = min;
    *pMax = max;
}

/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void )
{
    fprintf( stderr, "\nPress enter to exit.\n" );
    while (getchar() != '\n');
}

SurpriseRemoval Sample#

/*
This sample program demonstrates how to be informed about the removal of a device.

  Attention:
  If you build this sample in debug mode and run it using a GigE camera device, pylon will set the heartbeat
  timeout to 60 minutes. This is done to allow debugging and single stepping of the code without
  the camera thinking we're hung because we don't send any heartbeats.
  This also means that it would normally take 60 minutes for the application to notice that a GigE device
  has been disconnected.

  To work around this, the heartbeat timeout will be set to 1000 ms before we remove a device and wait to
  notice the removal.

*/

#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include <string.h>

#include <pylonc/PylonC.h>

#ifdef __GNUC__
#   include <unistd.h>
#   define Sleep(ms) usleep(ms*1000)
#endif


static int callbackCounter = 0;  /* Will be incremented by the callback function. */

/* Simple error handling */
#define CHECK( errc ) if ( GENAPI_E_OK != errc ) printErrorAndExit( errc )

/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void );

/* This method demonstrates how to retrieve the error message
for the last failed function call. */
void printErrorAndExit( GENAPIC_RESULT errc );


/* This function will be registered as a callback function that is called
when the opened device has been removed. On Windows only stdcall functions can be registered. */
void GENAPIC_CC removalCallbackFunction( PYLON_DEVICE_HANDLE hDevice );


/* Sets the heartbeat timeout. */
int64_t setHeartbeatTimeout( PYLON_DEVICE_HANDLE hDevice, int64_t timeout_ms );

int main( void )
{
    GENAPIC_RESULT              res;                /* Return value of pylon methods. */
    size_t                      numDevices;         /* Number of available devices.   */
    PYLON_DEVICE_HANDLE         hDev;               /* Handle for the pylon device.   */
    PYLON_DEVICECALLBACK_HANDLE hCb;                /* Required for deregistering the callback. */
    int                         loopCount;          /* Counter. */
    int                         isGigECamera;       /* 1 if the device is a GigE device. */

    /* Before using any pylon methods, the pylon runtime must be initialized. */
    PylonInitialize();

    /* Enumerate all camera devices. You must call
    PylonEnumerateDevices() before creating a device. */
    res = PylonEnumerateDevices( &numDevices );
    CHECK( res );
    if (0 == numDevices)
    {
        fprintf( stderr, "No devices found.\n" );
        /* Before exiting a program, PylonTerminate() should be called to release
        all pylon related resources. */
        PylonTerminate();
        pressEnterToExit();
        exit( EXIT_FAILURE );
    }

    /* Get a handle for the first device found.  */
    res = PylonCreateDeviceByIndex( 0, &hDev );
    CHECK( res );

    /* Before using the device, it must be opened. Open it for configuring
    parameters and for grabbing images. */
    res = PylonDeviceOpen( hDev, PYLONC_ACCESS_MODE_CONTROL | PYLONC_ACCESS_MODE_STREAM );
    CHECK( res );


    /* Print out the name of the camera we are using. */
    {
        char buf[256];
        size_t siz = sizeof( buf );
        _Bool isReadable;

        isReadable = PylonDeviceFeatureIsReadable( hDev, "DeviceModelName" );
        if (isReadable)
        {
            res = PylonDeviceFeatureToString( hDev, "DeviceModelName", buf, &siz );
            CHECK( res );
            printf( "Using camera %s\n", buf );
        }
    }

    /* Register the callback function. */
    res = PylonDeviceRegisterRemovalCallback( hDev, removalCallbackFunction, &hCb );
    CHECK( res );


    /* For GigE cameras, the application periodically sends heartbeat signals to the camera to keep the
    connection to the camera alive. If the camera doesn't receive heartbeat signals within the time
    period specified by the heartbeat timeout counter, the camera resets the connection.
    When the application is stopped by the debugger, the application cannot create the heartbeat signals.
    For that reason, the pylon runtime extends the heartbeat timeout when debugging to 5 minutes to allow
    debugging. For GigE cameras, we will set the heartbeat timeout to a shorter period before testing the
    callbacks.
    The heartbeat mechanism is also used for detection of device removal. When the pylon runtime doesn't
    receive an acknowledge for the heartbeat signal, it is assumed that the device has been removed. A
    removal callback will be fired in that case.
    By decreasing the heartbeat timeout, the surprise removal will be noticed earlier. */
    {
        /* Find out if we are using a GigE camera. */
        PylonDeviceInfo_t devInfo;
        res = PylonDeviceGetDeviceInfo( hDev, &devInfo );
        CHECK( res );
        isGigECamera = 0 == strcmp( devInfo.DeviceClass, "BaslerGigE" );

        /* Adjust the heartbeat timeout. */
        if (isGigECamera)
        {
            setHeartbeatTimeout( hDev, 1000 );  /* 1000 ms */
        }
    }

    /* Ask the user to disconnect a device. */
    loopCount = 20 * 4;
    printf( "Please disconnect the device (timeout %d s) \n", loopCount / 4 );


    /* Wait until the removal has been noticed and callback function has been fired. */
    do
    {
        /* Print a . every few seconds to tell the user we're waiting for the callback. */
        if (--loopCount % 4 == 0)
        {
            printf( "." );
            fflush( stdout );
        }

        Sleep( 250 );
    } while (callbackCounter < 1 && loopCount >= 0);  /*  Check loopCount so we won't wait forever. */


    if (callbackCounter < 1)
        printf( "\nTimeout expired. Device hasn't been removed.\n" );


    /* Clean up. */

    /* ... Deregister the removal callback. */
    res = PylonDeviceDeregisterRemovalCallback( hDev, hCb );
    CHECK( res );

    /* ....Close and release the pylon device. */
    res = PylonDeviceClose( hDev );
    CHECK( res );
    res = PylonDestroyDevice( hDev );
    CHECK( res );


    /* Shut down the pylon runtime system. Don't call any pylon method after
       calling PylonTerminate(). */
    PylonTerminate();
    pressEnterToExit();

    return EXIT_SUCCESS;
}



/* The function to be called when the removal of an opened device is detected. */
void GENAPIC_CC removalCallbackFunction( PYLON_DEVICE_HANDLE hDevice )
{
    PylonDeviceInfo_t   di;
    GENAPIC_RESULT      res;

    /* Print out the name of the device. It is not possible to read the name
    from the camera since it has been removed. Use the device's device
    information instead. For accessing the device information, no reading from
    the device is required. */

    /* Retrieve the device information for the removed device. */
    res = PylonDeviceGetDeviceInfo( hDevice, &di );
    CHECK( res );


    /* Print out the name. */
    printf( "\nCallback function for removal of device %s (%s).\n", di.FriendlyName, di.FullName );

    /* Increment the counter to indicate that the callback has been fired. */
    callbackCounter++;
}



/* If the device provides a heartbeat timeout, this function will set the heartbeat timeout.
   When the device provides the parameter, the old value is returned, -1 otherwise.
   The heartbeat timeout is a parameter provided by the transport layer.
   The transport layer parameters are exposed as a GenApi node map that
   can be retrieved from the device.
*/
int64_t setHeartbeatTimeout( PYLON_DEVICE_HANDLE hDev, int64_t timeout_ms )
{

    NODEMAP_HANDLE              hNodemap;   /* Handle to the node map */
    NODE_HANDLE                 hNode;      /* Handle to a node, i.e., a feature */
    GENAPIC_RESULT              res;        /* Return value */
    int64_t                     oldTimeout; /* The current timeout value */

    /* Get the node map for the transport layer parameters. */
    res = PylonDeviceGetTLNodeMap( hDev, &hNodemap );
    CHECK( res );
    if (GENAPIC_INVALID_HANDLE == hNodemap)
    {
        /* The device doesn't provide a transport layer node map. Nothing to do. */
        return -1;
    }
    /* Get the node for the heartbeat timeout parameter. */
    res = GenApiNodeMapGetNode( hNodemap, "HeartbeatTimeout", &hNode );
    CHECK( res );
    if (GENAPIC_INVALID_HANDLE == hNode)
    {
        /* There is no heartbeat timeout parameter. Nothing to do. */
        return -1;
    }

    /* Get the current value. */
    res = GenApiIntegerGetValue( hNode, &oldTimeout );
    CHECK( res );

    /* Set the new value. */
    res = GenApiIntegerSetValue( hNode, timeout_ms );
    CHECK( res );

    /* Return the old value. */
    return oldTimeout;
}

/* This function demonstrates how to retrieve the error message for the last failed
   function call. */
void printErrorAndExit( GENAPIC_RESULT errc )
{
    char* errMsg;
    size_t length;

    /* Retrieve the error message.
    ... Find out first how big the buffer must be, */
    GenApiGetLastErrorMessage( NULL, &length );
    errMsg = (char*) malloc( length );
    /* ... and retrieve the message. */
    GenApiGetLastErrorMessage( errMsg, &length );

    fprintf( stderr, "%s (%#08x).\n", errMsg, (unsigned int) errc );
    free( errMsg );

    /* Retrieve more details about the error.
    ... Find out first how big the buffer must be, */
    GenApiGetLastErrorDetail( NULL, &length );
    errMsg = (char*) malloc( length );
    /* ... and retrieve the message. */
    GenApiGetLastErrorDetail( errMsg, &length );

    fprintf( stderr, "%s\n", errMsg );
    free( errMsg );

    PylonTerminate();  /* Releases all pylon resources. */
    pressEnterToExit();

    exit( EXIT_FAILURE );
}

/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void )
{
    fprintf( stderr, "\nPress enter to exit.\n" );
    while (getchar() != '\n');
}

ActionCommands Sample#

/*
   This sample illustrates how to grab images
   using a GigE Vision action command to trigger multiple cameras.
   At least 2 connected GigE cameras are required for this sample.
*/

#ifndef _WIN32_WINNT
#   define _WIN32_WINNT 0x0400
#endif

#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <time.h>

#include <pylonc/PylonC.h>

/* Limits the amount of cameras used for grabbing.
It is important to manage the available bandwidth when grabbing with multiple
cameras. This applies, for instance, if two GigE cameras are connected to the
same network adapter via a switch. To manage the bandwidth, the GevSCPD
interpacket delay parameter and the GevSCFTD transmission delay parameter can
be set for each GigE camera device. The "Controlling Packet Transmission Timing
with the Interpacket and Frame Transmission Delays on Basler GigE Vision Cameras"
Application Note (AW000649xx000) provides more information about this topic. */
#define MAX_NUM_DEVICES 4

#define GIGE_PACKET_SIZE       1500 /* Size of one Ethernet packet. */
#define GIGE_PROTOCOL_OVERHEAD 36   /* Total number of bytes of protocol overhead. */

#define CHECK( errc ) if ( GENAPI_E_OK != errc ) printErrorAndExit( errc )

const uint32_t AllGroupMask = 0xffffffff;

/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void );

/* This method demonstrates how to retrieve the error message for the last failed function call. */
void printErrorAndExit( GENAPIC_RESULT errc );

int main( void )
{
    GENAPIC_RESULT              res;                      /* Return value of pylon methods. */
    size_t                      i;                        /* Generic loop variable */
    size_t                      numDevicesEnumerated;     /* Number of available devices. */
    size_t                      numDevicesToUse;          /* Number of usable devices. */
    _Bool                       isAvail;                  /* Used for checking feature availability. */
    size_t                      deviceIndex;              /* Index of device used in this sample. */
    PYLON_WAITOBJECTS_HANDLE    wos = NULL;               /* Wait objects. */
    uint32_t                    DeviceKey;                /* Random device key used in this session. It will be initialized below. */
    uint32_t                    GroupKey;                 /* Group key for the devices. In this sample all devices will be in the same group. */


    /* These are camera-specific variables. */
    PYLON_DEVICE_HANDLE         hDev[MAX_NUM_DEVICES];        /* Handle for the pylon device. */
    PYLON_STREAMGRABBER_HANDLE  hGrabber[MAX_NUM_DEVICES];    /* Handle for the pylon stream grabber. */
    unsigned char*              buffers[MAX_NUM_DEVICES];     /* Buffers used for grabbing. */
    PYLON_STREAMBUFFER_HANDLE   bufHandles[MAX_NUM_DEVICES];  /* Handles for the buffers. */
    PylonDeviceInfo_t           deviceInfos[MAX_NUM_DEVICES]; /* Information about enumerated devices */

    /* Seed the random number generator. */
    srand( (unsigned int) time( NULL ) );

    /* Get random device key used in this session. */
    DeviceKey = rand();
    /* In this sample all cameras will belong to the same group. */
    GroupKey = 0x24;

    /* Before using any pylon methods, the pylon runtime must be initialized. */
    PylonInitialize();

    /* Enumerate all devices. You must call
    PylonEnumerateDevices() before creating a device. */
    res = PylonEnumerateDevices( &numDevicesEnumerated );
    CHECK( res );
    if (numDevicesEnumerated == 0)
    {
        fprintf( stdout, "No devices found!\n" );
        PylonTerminate();
        pressEnterToExit();
        exit( EXIT_FAILURE );
    }

    /* Create wait objects so we can wait for the images of each camera */
    res = PylonWaitObjectsCreate( &wos );
    CHECK( res );

    /* =======================================================================
     *  Open Cameras and Set Parameters.
     * ======================================================================= */
    for (deviceIndex = 0; deviceIndex < numDevicesEnumerated; ++deviceIndex)
    {
        PylonDeviceInfo_t di;

        res = PylonGetDeviceInfo( deviceIndex, &di );
        CHECK( res );

        if (strcmp( di.DeviceClass, "BaslerGigE" ) != 0)
        {
            /* Action commands are only supported by GigE cameras. */
            continue;
        }

        /* Get a handle for the device. */
        res = PylonCreateDeviceByIndex( deviceIndex, &hDev[deviceIndex] );
        CHECK( res );

        /* Before using the device, it must be opened. Open it for setting
        parameters and for grabbing images. */
        res = PylonDeviceOpen( hDev[deviceIndex], PYLONC_ACCESS_MODE_CONTROL | PYLONC_ACCESS_MODE_STREAM );
        CHECK( res );

        /* When the device has been opened successfully, we remember its deviceinfo
           so we can print out the name device name, etc. later */
        deviceInfos[deviceIndex] = di;

        /* Print out the name of the camera we are using. */
        printf( "Using camera '%s'\n", deviceInfos[deviceIndex].ModelName );

        isAvail = PylonDeviceFeatureIsAvailable( hDev[deviceIndex], "ActionControl" );
        if (!isAvail)
        {
            /* Action Command feature is not available. */
            fprintf( stderr, "Device doesn't support the Action Control" );
            PylonTerminate();
            pressEnterToExit();
            exit( EXIT_SUCCESS );
        }

        /* Configure the first action (Action1)*/
        res = PylonDeviceSetIntegerFeatureInt32( hDev[deviceIndex], "ActionSelector", 1 );
        CHECK( res );
        res = PylonDeviceSetIntegerFeatureInt32( hDev[deviceIndex], "ActionDeviceKey", DeviceKey );
        CHECK( res );
        res = PylonDeviceSetIntegerFeatureInt32( hDev[deviceIndex], "ActionGroupKey", GroupKey );
        CHECK( res );
        res = PylonDeviceSetIntegerFeature( hDev[deviceIndex], "ActionGroupMask", AllGroupMask );
        CHECK( res );

        /* Set the trigger mode to FrameStart and TriggerSource to first action. */
        res = PylonDeviceFeatureFromString( hDev[deviceIndex], "TriggerSelector", "FrameStart" );
        CHECK( res );
        res = PylonDeviceFeatureFromString( hDev[deviceIndex], "TriggerMode", "On" );
        CHECK( res );
        res = PylonDeviceFeatureFromString( hDev[deviceIndex], "TriggerSource", "Action1" );  /* Action1 corresponds to ActionSelector=1. */
        CHECK( res );

        /* Set the pixel format to Mono8, where gray values will be output as 8-bit values for each pixel. */
        /* ... First check to see if the device supports the Mono8 format. */
        isAvail = PylonDeviceFeatureIsAvailable( hDev[deviceIndex], "EnumEntry_PixelFormat_Mono8" );
        if (!isAvail)
        {
            /* Feature is not available. */
            fprintf( stderr, "Device doesn't support the Mono8 pixel format.\n" );
            PylonTerminate();
            pressEnterToExit();
            exit( EXIT_SUCCESS );
        }

        /* ... Set the pixel format to Mono8. */
        res = PylonDeviceFeatureFromString( hDev[deviceIndex], "PixelFormat", "Mono8" );
        CHECK( res );

        /* For GigE cameras, we recommend increasing the packet size for better
        performance. When the network adapter supports jumbo frames, set the packet
        size to a value > 1500, e.g. to 8192. In this sample, we only set the packet size
        to 1500.

        Also we set the Inter-Packet and the Frame Transmission delay
        so that the switch can line up packets in a better way.
        */

        res = PylonDeviceSetIntegerFeature( hDev[deviceIndex], "GevSCPSPacketSize", GIGE_PACKET_SIZE );
        CHECK( res );

        res = PylonDeviceSetIntegerFeature( hDev[deviceIndex], "GevSCPD", (GIGE_PACKET_SIZE + GIGE_PROTOCOL_OVERHEAD) * (MAX_NUM_DEVICES - 1) );
        CHECK( res );

        res = PylonDeviceSetIntegerFeature( hDev[deviceIndex], "GevSCFTD", (GIGE_PACKET_SIZE + GIGE_PROTOCOL_OVERHEAD) * deviceIndex );
        CHECK( res );
    }

    /* Remember the number of devices actually created. */
    numDevicesToUse = deviceIndex;

    if (numDevicesToUse == 0)
    {
        fprintf( stderr, "No suitable cameras found!\n" );
        PylonTerminate();
        pressEnterToExit();
        exit( EXIT_SUCCESS );
    }

    if (numDevicesToUse < 2)
    {
        printf( "WARNING: This sample works best with two or more GigE cameras supporting action commands.\n" );
    }

    /*  ======================================================================
        Allocate and Register Buffers for Grab.
        ====================================================================== */
    for (deviceIndex = 0; deviceIndex < numDevicesToUse; ++deviceIndex)
    {
        PYLON_WAITOBJECT_HANDLE hWait;
        size_t payloadSize;




        /* Image grabbing is done using a stream grabber.
          A device may be able to provide different streams. A separate stream grabber must
          be used for each stream. In this sample, we create a stream grabber for the default
          stream, i.e., the first stream ( index == 0 ).
          */

        /* Get the number of streams supported by the device and the transport layer. */
        res = PylonDeviceGetNumStreamGrabberChannels( hDev[deviceIndex], &i );
        CHECK( res );
        if (i < 1)
        {
            fprintf( stderr, "The transport layer doesn't support image streams.\n" );
            PylonTerminate();
            pressEnterToExit();
            exit( EXIT_FAILURE );
        }

        /* Create and open a stream grabber for the first channel. */
        res = PylonDeviceGetStreamGrabber( hDev[deviceIndex], 0, &hGrabber[deviceIndex] );
        CHECK( res );
        res = PylonStreamGrabberOpen( hGrabber[deviceIndex] );
        CHECK( res );

        /* Get a handle for the stream grabber's wait object. The wait object
           allows waiting for buffers to be filled with grabbed data. */
        res = PylonStreamGrabberGetWaitObject( hGrabber[deviceIndex], &hWait );
        CHECK( res );

        /* Add the stream grabber's wait object to our wait objects.
           This is needed to be able to wait until at least one camera has
           grabbed an image in the grab loop below. */
        res = PylonWaitObjectsAdd( wos, hWait, NULL );
        CHECK( res );

        /* Determine the required size of the grab buffer. */
        res = PylonStreamGrabberGetPayloadSize( hDev[deviceIndex], hGrabber[deviceIndex], &payloadSize );
        CHECK( res );


        /* Allocate memory for grabbing.  */
        buffers[deviceIndex] = (unsigned char*) malloc( payloadSize );
        if (NULL == buffers[deviceIndex])
        {
            fprintf( stderr, "Out of memory.\n" );
            PylonTerminate();
            pressEnterToExit();
            exit( EXIT_FAILURE );
        }

        /* We must tell the stream grabber the number and size of the buffers
            we are using. */
        /* .. We will use one buffer for grabbing. */
        res = PylonStreamGrabberSetMaxNumBuffer( hGrabber[deviceIndex], 1 );
        CHECK( res );
        /* .. We will not use buffers bigger than payloadSize bytes. */
        res = PylonStreamGrabberSetMaxBufferSize( hGrabber[deviceIndex], payloadSize );
        CHECK( res );


        /*  Allocate the resources required for grabbing. After this, critical parameters
            that impact the payload size must not be changed until FinishGrab() is called. */
        res = PylonStreamGrabberPrepareGrab( hGrabber[deviceIndex] );
        CHECK( res );


        /* Before using the buffers for grabbing, they must be registered at
           the stream grabber. For each registered buffer, a buffer handle
           is returned. After registering, these handles are used instead of the
           raw pointers. */
        res = PylonStreamGrabberRegisterBuffer( hGrabber[deviceIndex], buffers[deviceIndex], payloadSize, &bufHandles[deviceIndex] );
        CHECK( res );

        /* Feed the buffers into the stream grabber's input queue. For each buffer, the API
           allows passing in a pointer to additional context information. This pointer
           will be returned unchanged when the grab is finished. In our example, we use the index of the
           camera as context information. */
        res = PylonStreamGrabberQueueBuffer( hGrabber[deviceIndex], bufHandles[deviceIndex], (void*) deviceIndex );
        CHECK( res );
    }

    /* The stream grabber is now prepared. As soon as the camera starts acquiring images,
       the image data will be grabbed into the provided buffers.  */
    for (deviceIndex = 0; deviceIndex < numDevicesToUse; ++deviceIndex)
    {
        /* Let the camera acquire images. */
        res = PylonDeviceExecuteCommandFeature( hDev[deviceIndex], "AcquisitionStart" );
        /* Do not call CHECK() here! Instead exit the loop. */
        if (res != GENAPI_E_OK)
        {
            break;
        }
    }


    /*  ======================================================================
        Issue an ActionCommand and Retrieve the Images.
        ====================================================================== */

    /* Only start the grab loop if all cameras have been "started" */
    if (res == GENAPI_E_OK)
    {
        char subnet[32] = { '\0' }; /* reserve space for a dotted ip address */
        size_t buflen = sizeof subnet;
        PYLON_DEVICE_INFO_HANDLE hDevInfo = NULL;

        /* Retrieve subnet broadcast address of first device.
           We must query this GigE specific value explicitly as it isn't contained
           in the generic PylonDeviceInfo_t struct. */
        res = PylonDeviceGetDeviceInfoHandle( hDev[0], &hDevInfo );
        CHECK( res );
        res = PylonDeviceInfoGetPropertyValueByName( hDevInfo, "SubnetAddress", subnet, &buflen );
        CHECK( res );


        /* Trigger the camera using an action command. */
        res = PylonGigEIssueActionCommand( DeviceKey, GroupKey, AllGroupMask, subnet, 0, NULL, NULL );
        CHECK( res );

        /* Grab one image from each camera. */
        for (deviceIndex = 0; deviceIndex < numDevicesToUse; ++deviceIndex)
        {
            _Bool isReady;
            size_t woIndex = 0;
            PylonGrabResult_t grabResult;

            /* Wait for the next buffer to be filled. Wait up to 5000 ms.*/
            res = PylonWaitObjectsWaitForAny( wos, 5000, &woIndex, &isReady );
            CHECK( res );
            if (!isReady)
            {  /* Timeout occurred. */

                /* Grab timeout occurred. */
                fprintf( stderr, "Grab timeout occurred.\n" );
                break; /* Stop grabbing. */
            }

            /* The woIndex corresponds to the index of the camera in handle arrays. */

            /* Retrieve the grab result. */
            res = PylonStreamGrabberRetrieveResult( hGrabber[woIndex], &grabResult, &isReady );
            CHECK( res );
            if (!isReady)
            {
                /* Oops. No grab result available? We should never have reached this point.
                   Since the wait operation above returned without a timeout, a grab result
                   should be available. */
                fprintf( stderr, "Failed to retrieve a grab result\n" );
                break;
            }

            /* Check to see whether the image was grabbed successfully. */
            if (grabResult.Status == Grabbed && grabResult.PayloadType == PayloadType_Image)
            {
                /*  Success. Perform image processing. Since we passed more than one buffer
                to the stream grabber, the remaining buffers are filled while
                we are doing the image processing. The processed buffer won't be touched by
                the stream grabber until we pass it back to the stream grabber. */

                /* Pointer to the buffer attached to the grab result.
                   Get the buffer pointer from the result structure. Since we also got the buffer index,
                   we could alternatively use buffers[bufferIndex]. */
                /* Unsigned char* buffer = (unsigned char*) grabResult.pBuffer; */


                /* Perform processing. */
                printf( "Grabbed frame from camera %u into buffer.\n", (unsigned int) woIndex );

#ifdef GENAPIC_WIN_BUILD
                /* Display image. */
                if (woIndex <= 31)
                {
                    res = PylonImageWindowDisplayImageGrabResult( woIndex, &grabResult );
                    CHECK( res );
                }
#endif
            }
            else if (grabResult.Status == Failed)
            {
                /* If a buffer has been incompletely grabbed, the network bandwidth is possibly insufficient for transferring
                   multiple images simultaneously. See note on MAX_NUM_DEVICES. */
                fprintf( stderr, "Frame wasn't grabbed successfully.  Error code = 0x%08X\n",
                         grabResult.ErrorCode );
            }
        } /* for */
    }

    /* ========================================================================
        Clean up.
       ======================================================================== */

    /* Stop the image acquisition on the cameras. */

    for (deviceIndex = 0; deviceIndex < numDevicesToUse; ++deviceIndex)
    {
        /*  ... Stop the camera. */
        res = PylonDeviceExecuteCommandFeature( hDev[deviceIndex], "AcquisitionStop" );
        CHECK( res );
    }

    /* Remove all wait objects from waitobjects. */
    res = PylonWaitObjectsRemoveAll( wos );
    CHECK( res );
    res = PylonWaitObjectsDestroy( wos );
    CHECK( res );


    /* Do the cleanup for each camera we've set up. */
    for (deviceIndex = 0; deviceIndex < numDevicesToUse; ++deviceIndex)
    {
        _Bool rdy;
        PylonGrabResult_t grabResult;

        /* Issue a flush call to ensure that all queued buffers are put into the
           stream grabber's output queue. */
        res = PylonStreamGrabberFlushBuffersToOutput( hGrabber[deviceIndex] );
        CHECK( res );

        /* The buffers can now be retrieved from the stream grabbers output queue. */
        do
        {
            res = PylonStreamGrabberRetrieveResult( hGrabber[deviceIndex], &grabResult, &rdy );
            CHECK( res );
        } while (rdy);

        /* After all buffers have been retrieved from the stream grabber, they can be deregistered.
           After deregistering the buffers, it is safe to free the memory. */
        res = PylonStreamGrabberDeregisterBuffer( hGrabber[deviceIndex], bufHandles[deviceIndex] );
        CHECK( res );
        free( buffers[deviceIndex] );

        /* Release grabbing related resources. */
        res = PylonStreamGrabberFinishGrab( hGrabber[deviceIndex] );
        CHECK( res );

        /* When PylonStreamGrabberFinishGrab() has been called, parameters that impact the payload size (e.g.,
        the AOI width and height parameters) are unlocked and can be modified again. */

        /* Close the stream grabber. */
        res = PylonStreamGrabberClose( hGrabber[deviceIndex] );
        CHECK( res );


        /* Close and release the pylon device. The stream grabber becomes invalid
           after closing the pylon device. Don't call stream grabber related methods after
           closing or releasing the device. */
        res = PylonDeviceClose( hDev[deviceIndex] );
        CHECK( res );


        res = PylonDestroyDevice( hDev[deviceIndex] );
        CHECK( res );
    }

    pressEnterToExit();

    /* ... Shut down the pylon runtime system. Don't call any pylon function after
       calling PylonTerminate(). */
    PylonTerminate();

    return EXIT_SUCCESS;

}

/* This method demonstrates how to retrieve the error message for the last failed
   function call. */
void printErrorAndExit( GENAPIC_RESULT errc )
{
    char* errMsg;
    char* errDetail;
    size_t length = 0;

    /* Retrieve the error message using double calling. */
    GenApiGetLastErrorMessage( NULL, &length );
    errMsg = (char*) malloc( length );
    GenApiGetLastErrorMessage( errMsg, &length );

    /* Get the detailed error message. */
    length = 0;
    GenApiGetLastErrorDetail( NULL, &length );
    errDetail = (char*) malloc( length );
    GenApiGetLastErrorDetail( errDetail, &length );


    fprintf( stderr, "%s (%#08x).\n%s\n", errMsg, (unsigned int) errc, errDetail );

    free( errDetail );
    free( errMsg );

    PylonTerminate();  /* Releases all pylon resources. */
    pressEnterToExit();

    exit( EXIT_FAILURE );
}

/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void )
{
    fprintf( stderr, "\nPress enter to exit.\n" );
    while (getchar() != '\n');
}