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