Skip to content

fglib5#

The library fglib5 manages initialization and control of frame grabbers in the computer.

To use the library, the include files fgrab_define.h, fgrab_struct.h and fgrab_prototyp.h should be added to the source code.

#include <fgrab_define.h>
#include <fgrab_struct.h>
#include <fgrab_prototyp.h>

Additionally, fglib5.lib should be added to your Microsoft Visual Studio Project, or libfglib5.so to your Linux project. If you use CMake, the package name is FgLib5. CMake will store the include directory in the variable ${FgLib5_INCLUDE_DIR} and the libraries in ${FgLib5_LIBRARIES}. See Prerequisites for more details on projects and how to use CMake.

Error Handling in the Frame Grabber Library#

const char * Fg_getErrorDescription(
    Fg_Struct * fg,
    int result);

int Fg_getLastErrorNumber(
    Fg_Struct * fg);

Most functions of the API return an int result code. If the function call was executed successfully, the return value will be FG_OK. A negative value denotes an error condition in most cases. The error codes are defined in the header file fgrab_define.h.

The function Fg_getErrorDescription() can be used to get a string representation of a specific result code. The first argument to this function, a handle to a frame grabber, is not used and is only included for API backwards compatibility. You should always pass nullptr.

The function Fg_getLastErrorNumber() will always return the result code of the last function called in the same thread context. This is most useful in cases when a function does not return a result code itself. The function accepts a frame grabber handle as an argument, which can be nullptr in case the last function called did not receive a frame grabber handle. To request the result code of a function that requires a frame grabber handle, the same handle has to be passed to Fg_getLastErrorNumber().

Info

Error codes are stored in thread-local storage. This means that by calling Fg_getLastErrorNumber() from one thread the application cannot request error codes which occurred in another thread.

The following code will print the last error which happened in a context not specific to a frame grabber handle, and a description:

int result = Fg_getLastErrorNumber(nullptr);
if (result != FG_OK) {
    const char * description = Fg_getErrorDescription(nullptr, result);

    std::cout << "Error " << result
              << ": " << description
              << std::endl;
}

The code examples in the rest of this part of the documentation will not include error handling as this is specific to the requirements of the application. As much as is feasible, return codes will be checked however for successful execution of the functions.

Frame Grabber Library Initialization#

int Fg_InitLibraries(
    const char *);

void Fg_FreeLibraries();

Before using the library fglib5, it should be initialized by calling the function Fg_InitLibraries(). The function accepts an argument which is used for internal purposes only and should be set to nullptr.

After the application is no longer using the library, the function Fg_FreeLibraries() should be called to release any resources allocated by the previous initialization.

int result = Fg_InitLibraries(nullptr);
if (result != FG_OK) {
    // handle error ...
}

// use frame grabber(s) ...

Fg_FreeLibraries();

System Information#

Before initializing a frame grabber, information about the version of the Runtime SDK which is used, the number and type of frame grabbers installed in the computer and other information can be requested.

Runtime SDK Version#

const char * Fg_getSWVersion();

A string representation of the version of the Runtime SDK can be requested using Fg_getSWVersion().

const char * rtVersion = Fg_getSWVersion();
std::cout << "Runtime SDK version: " << rtVersion
          << std::endl;

General System Information#

int Fg_getIntSystemInformationGlobal(
    Fg_Info_Selector information,
    FgProperty propertyId,
    int * value);

Info

The function Fg_getIntSystemInformationGlobal() was added in version 5.9 of the Runtime SDK. See section System Information in Plain C for the old interface.

The following information can be requested through the function Fg_getIntSystemInformationGlobal() using the property PROP_ID_VALUE:

Information Description Type
INFO_NR_OF_BOARDS The number of boards found in the computer int32_t
INFO_MAX_NR_OF_BOARDS Maximum number of boards supported int32_t
INFO_SERVICE_ISRUNNING 0: service is not running; 1: service is running int32_t

For example, the number of frame grabbers installed in the computer can be requested as shown below:

int numBoards = 0;
int result = Fg_getIntSystemInformationGlobal(INFO_NR_OF_BOARDS, PROP_ID_VALUE, &numBoards);
if (result == FG_OK) {
    std::cout << "Number of boards: " << numBoards
              << std::endl;
}

Board-Specific System Information#

int Fg_getIntSystemInformationForBoardIndex(
    unsigned int board,
    Fg_Info_Selector information,
     FgProperty propertyId,
     int * value);

int Fg_getInt64SystemInformationForBoardIndex(
    unsigned int board,
    Fg_Info_Selector information,
    FgProperty propertyId,
    int64_t * value);

int Fg_getStringSystemInformationForBoardIndex(
    unsigned int board,
    Fg_Info_Selector information,
    FgProperty propertyId,
    std::string & value,
    const std::string & arg = "");

int Fg_getIntSystemInformationForFgHandle(
    Fg_Struct * fg,
    Fg_Info_Selector information,
    FgProperty propertyId,
    int * value);

int Fg_getInt64SystemInformationForFgHandle(
    Fg_Struct * fg,
    Fg_Info_Selector information,
    FgProperty propertyId,
    int64_t * value);

int Fg_getStringSystemInformationForFgHandle(
    Fg_Struct * fg,
    Fg_Info_Selector information,
    FgProperty propertyId,
    std::string & value,
    const std::string & arg = "");

Info

The functions documented in this chapter were added in version 5.9 of the Runtime SDK. See section System Information in Plain C for the old interface.

For any given frame grabber, most other information can be requested through the functions Fg_getIntSystemInformationForBoardIndex(), Fg_getInt64SystemInformationForBoardIndex() and Fg_getStringSystemInformationForBoardIndex() by using the board index or frame grabber handle and the property PROP_ID_VALUE:

Information Description Type
INFO_TIMESTAMP_FREQUENCY The timestamp frequency used for the image timestamps int64_t
INFO_BOARDNAME Board name as shown e.g. in microDiagnostics string
INFO_BOARDTYPE Board type as defined in sisoboards.h int32_t
INFO_BOARDSERIALNO Board serial number int32_t
INFO_FIRMWAREVERSION Firmware version of the board string
INFO_HARDWAREVERSION Hardware version of the board string
INFO_CAMERA_INTERFACE Camera interface provided by the board ('CameraLink', 'CXP', 'GigE' or 'CameraLinkHS') string
INFO_DRIVERVERSION Driver version used for the board string
INFO_DRIVERARCH Driver architecture used for the board string
INFO_DRIVERFULLVERSION Full driver version including architecture used for the board string
INFO_DRIVERGROUPAFFINITY The driver IRQ group affinity (see Support for Non-Uniform Memory Access) int32_t
INFO_DRIVERAFFINITYMASK The driver IRQ processor affinity mask (see Support for Non-Uniform Memory Access) int64_t
INFO_LICENSE_GROUP_CODE Frame grabber license group code (must be a superset of the applet license group codea) int32_t
INFO_LICENSE_USER_CODE Frame grabber license user code (must match the applet license user code) int32_t
INFO_IS_POCL 0: the board does not support PoCL; 1: the board supports PoCL int32_t
INFO_NR_OF_CXP_PORTS Number of ports on a board with 'CXP' interface int32_t
INFO_NR_OF_CL_PORTS Number of ports on a board with 'CameraLink' interface int32_t
INFO_NR_OF_CLHS_PORTS Number of ports on a board with 'CameraLinkHS' interface int32_t
INFO_NR_OF_GIGE_PORTS Number of ports on a board with 'GigE' interface int32_t
INFO_APPLET_DESIGN_ID The HAP id of an applet (the applet path must be passed in the parameter arg) string
INFO_APPLET_BITSTREAM_ID The bit stream id of an applet (the applet path must be passed in the parameter arg) string
INFO_STATUS_PCI_LINK_WIDTH Number of PCIe lanes used by the frame grabber int32_t
INFO_STATUS_PCI_EXPECTED_LINK_WIDTH Number of PCIe lanes supported by the frame grabber int32_t
INFO_STATUS_PCI_LINK_SPEED PCIe generation used by the frame grabber int32_t
INFO_STATUS_PCI_EXPECTED_LINK_WIDTH PCIe generation supported by the frame grabber int32_t
INFO_STATUS_PCI_PAYLOAD_SIZE PCIe payload size used by the frame grabber int32_t

The list above is not complete and contains only the properties for the information useful to the application. See the SDK reference for more use cases of the API.

For example, the following code requests the board type and name.

const int boardIndex = 0;

int boardType = 0;
int result =
    Fg_getIntSystemInformationForBoardIndex(boardIndex, INFO_BOARDTYPE,
                                            PROP_ID_VALUE, &boardType);

std::string boardName;
if (result == FG_OK) {
    result =
        Fg_getStringSystemInformationForBoardIndex(boardIndex, INFO_BOARDNAME,
                                                   PROP_ID_VALUE, boardName);
}

if (result == FG_OK) {
    std::cout << "Board #" << boardIndex
              << " is a " << boardName
              << " (type " << std::hex << boardType
              << std::dec << ")" << std::endl;
}

Once a frame grabber handle is available after initialization, the functions Fg_getIntSystemInformationForFgHandle(), Fg_getInt64SystemInformationForFgHandle() and Fg_getStringSystemInformationForFgHandle() can be used to get information specific to the board according to the list above. (See chapter Frame Grabber Initialization.) The following information can be requested using the frame grabber handle and the property PROP_ID_VALUE:

Information Description Type
INFO_APPLET_CAPABILITY_TAGS A list of key-value-pairs describing the applet features string
INFO_OWN_BOARDINDEX The board index of the frame grabber int32_t
INFO_FPGA_BITSTREAM_ID The bit stream id for the applet active in the FPGA string
INFO_APPLET_FULL_PATH The full applet path string
INFO_APPLET_FILE_NAME The applet file name string
INFO_APPLET_TYPE 0: HAP file; 1: DLL/SO file (see Applets) int32_t

The list above is not complete and contains only the properties for the information useful to the application. See the SDK reference for more use cases of the API.

Using the Board Index#

A frame grabber is selected by using the board index. If you know the number of boards present in the system, in the default case, the first board is identified by the index 0, the second board by the index 1 and so on.

The runtime allows the user to assign a unique index to each board using a configuration file, which can be generated from within microDiagnostics. In that case, the user should know their assignments and according board indices.

Applets#

To work with the frame grabber, an applet is needed. An applet is a collection of several things, most generally:

  • a design for the FPGA on the frame grabber board which implements the image processing from receiving images from a camera device to sending the processed image into the computer memory
  • a description of the design for the software which contains the Visual Applets operators used in the design and the information to map the parameters to FPGA registers
  • a collection of software interface libraries which serve as generators for class instances for handling the translation of operator parameter values to and from FPGA register contents
  • a top-level library, VAS, which handles instantiating all operator classes, initialization and interfacing with the applet

Applets come in the form of a HAP file if they were designed using Visual Applets, or as a wrapped applet library file if they come preinstalled by the runtime. HAP files are usually stored inside a board-specific subdirectory of the Hardware Applets directory in the runtime installation, while wrapped applet library files are installed in a board-specific subdirectory of the dll directory. These subdirectories are referred to as standard locations for the corresponding type of applet throughout this documentation.

Each applet provides a set of parameters to configure the provided features. Consult the documentation for the specific applet to be used for an in-depth description of the features and parameters provided by the applet.

Choosing the Right Applet#

Applets which can be installed with the runtime usually come as a wrapped library file. The naming convention for those files follows some rules:

  • The file name usually starts with Acq_. (These applets are referred to as Advanced Acquisition Applet, the previous generation was referred to as Standard Applet and had a different naming convention.)
  • Next, the number of cameras supported by the applet is given in the form of Single, Dual or Quad.
  • Then, the camera interface supported is given. For example, CXP12 indicates the CoaXPress camera interface with up to 12 Gbit/s as specified in the CoaXPress Standard Version 2.0.
  • On older frame grabber platforms, the maximum number of camera link connections is given in the form of x1, x2 or x4. (This was dropped from the naming convention as x4 includes support for x2 and x1, x2 supports x1 and the maximum number of links supported usually should be clear from the number of cameras supported and the number of physical ports the frame grabber provides.)
  • Then, the sensor type supported is given. This is either Area or Line. For applets supporting both area and line type sensors, this is dropped.
  • The last part of the name is the data format supported by the applet, like Gray8, Bayer16, RGB24. For applets supporting multiple data formats, this is dropped.

For example, the applet Acq_SingleCXP12Area is an Advanced Acquisition Applet which supports one CXP camera with up to 12 Gbit/s and an area sensor. The applet supports multiple data formats. To understand the supported data formats and sensor sizes the applet documentation has to be consulted.

If the application for example requires two CXP cameras with 12 Gbit/s, a line type sensor with 10-bit gray scale output, look for applets called Acq_DualCXP12Line, Acq_DualCXP12LineGray10 or Acq_DualCXP12LineGray16 and read the accompanying documentation. If the cameras connect using only a single physical link, the Quad variants of the applets can be considered as well.

Enumerating Applets#

int Fg_getAppletIterator(
    int board,
    FgAppletIteratorSource source,
    Fg_AppletIteratorType * iter,
    int flags);

Fg_AppletIteratorItem Fg_getAppletIteratorItem(
    Fg_AppletIteratorType iter,
    int index);

int64_t Fg_getAppletIntProperty(
    Fg_AppletIteratorItem item,
    FgAppletIntProperty property);

const char * Fg_getAppletStringProperty(
    Fg_AppletIteratorItem item,
    FgAppletIntProperty property);

int Fg_freeAppletIterator(
    Fg_AppletIteratorType iter);

Usually, an application will use one or maybe a few specific applets and the features will be known through the applet documentation and the following functions will not be useful to most applications. In cases where an application is designed more dynamically, the Runtime SDK provides functions through which the applets can be enumerated and various information about applets can be requested.

To enumerate Applets, the function Fg_getAppletIterator() can be called. If you want to enumerate the applets which are available on the runtime installation, the source FG_AIS_FILESYSTEM should be used. To enumerate only applets which can be loaded on the given board, pass FG_AF_IS_LOADABLE for the flags. This ensures that any applets returned by the API can be used by a subsequent call to initialize the frame grabber. The function returns the number of items in the iterator.

The items of the applet iterator can be retrieved by calling Fg_getAppletIteratorItem().

The following information can be requested from an item by calling Fg_getAppletIntProperty() or Fg_getAppletStringProperty():

Property Description Type
FG_AP_INT_FLAGS Flags which apply to the applet (FG_AF_… constants) int32_t
FG_AP_INT_INFO Tags which apply to the applet (FG_AI_… constants) int32_t
FG_AP_INT_PARTITION Partition on which the applet is flashed (mE5 only) int32_t
FG_AP_INT_NR_OF_DMA Maximum number of DMA channels provided by the applet int32_t
FG_AP_INT_NR_OF_CAMS Maximum number of cameras accessible when using the applet int32_t
FG_AP_INT_GROUP_CODE Applet license group code (must be a subset of the frame grabber license group codea) int32_t
FG_AP_INT_USER_CODE Applet license user code (must match the frame grabber license user code) int32_t
FG_AP_INT_DESIGN_VERSION Major version of the FPGA design int32_t
FG_AP_INT_DESIGN_REVISION Version revision of the FPGA design int32_t
FG_AP_STRING_APPLET_UID UID identifying the applet file string
FG_AP_STRING_BITSTREAM_UID UID identifying the FPGA design string
FG_AP_STRING_DESIGN_NAME Name of the FPGA design string
FG_AP_STRING_APPLET_NAME Name of the applet string
FG_AP_STRING_DESCRIPTION Description of the applet string
FG_AP_STRING_CATEGORY Category of the applet string
FG_AP_STRING_APPLET_PATH Full path of the applet string
FG_AP_STRING_SUPPORTED_PLATFORMS Comma separated list of supported platforms string
FG_AP_STRING_TAGS Comma separated list of tags string
FG_AP_STRING_VERSION Version of the applet string
FG_AP_STRING_APPLET_FILE File name of the applet string
FG_AP_STRING_RUNTIME_VERSION Required Runtime SDK version string

The list above is not complete and contains only the properties for the information useful to the application. See the SDK reference for more use cases of the API.

After using the applet iterator, it should be released by calling Fg_freeAppletIterator().

The applet name should be sufficient for a subsequent call to initialize the frame grabber, as long as the name is unique and the applets are located in one of the standard locations. If multiple applets use the same name, or an applet is located outside of one of the standard locations, then the full path should be used. The following example shows how to extract the applet name from all applets found in the standard locations:

const in boardIndex = 0;

Fg_AppletIteratorType iter = 0;
int numItems =
    Fg_getAppletIterator(boardIndex, FG_AIS_FILESYSTEM,
                         &iter, FG_AF_IS_LOADABLE);

if (numItems >= 0) {
    std::cout << "Found " << numItems << " applets";

    for (int i = 0; i < numItems; ++i) {
        auto item = Fg_getAppletIteratorItem(iter, i);

        const char * appletName =
            Fg_getAppletStringProperty(item, FG_AP_STRING_APPLET_NAME);

        std::cout << " " << appletName;
    }

    std::cout << std::endl;
    Fg_freeAppletIterator(iter);
}

Frame Grabber Initialization#

Fg_Struct * Fg_Init(
    const char * applet,
    unsigned int board);

int Fg_FreeGrabber(
    Fg_Struct * fg);

To work with a frame grabber, it has to be initialized first. This is done by loading an applet using the function Fg_Init(). The function returns a handle to the frame grabber for the given board index after initializing the frame grabber with the applet specified. The handle is used in most of the API functions described in the following chapters.

After the application is done using the frame grabber, it should be released by calling Fg_FreeGrabber().

For example, the following code initializes a CXP-12 frame grabber with the applet Acq_SingleCXP12Area:

const int boardIndex = 0;
const char * applet = "Acq_SingleCXP12Area";

Fg_Struct * fg = Fg_Init(applet, boardIndex);
if (fg != nullptr) {
    // use frame grabber ...

    Fg_FreeGrabber(fg);
}

Configuration Files#

Fg_Struct * Fg_InitConfig(
    const char * config,
    unsigned int board);

int Fg_loadConfig(
    Fg_Struct * fg,
    const char * config);

int Fg_saveConfig(
    Fg_Struct * fg,
    const char * config);

If you use microDisplayX to initially configure a frame grabber and test your setup, the configuration can be saved from within the program. The main configuration file with have an extension .mcf and a second file with an extension .mfs will be created alongside. With these two files, instead of replicating the configuration through API calls, the application can directly initialize a frame grabber using these two files.

Configuration in this context refers to the applet with which a frame grabber was initialized, and the settings of the parameters of the applet. See chapter Working with Applet Parameters for more information.

Info

Only the main configuration file with the extension .mcf is used in the parameters to the functions. The file with extension .mfs will be used automatically as long as the file name up to the extension is the same.

To initialize a frame grabber from configuration files, the function Fg_InitConfig() can be called instead of Fg_Init(). The function returns a handle to the frame grabber for the given board index after initializing the frame grabber according to the main configuration file specified.

After the application is done using the frame grabber, it should be released by calling Fg_FreeGrabber().

If the application requires different sets of configurations, these can be applied to a frame grabber by calling Fg_loadConfig() at any point after calling Fg_Init() or Fg_InitConfig().

The current configuration can be written to configuration files by calling Fg_saveConfig() at any point after calling Fg_Init() or Fg_InitConfig().

The following example shows how to save and restore the configuration of a frame grabber:

const char * config = "SavedState.mcf";

int result = Fg_saveConfig(fg, config);
if (result != FG_OK) {
    // handle error ...
}

// change frame grabber configuration ...

result = Fg_loadConfig(fg, config);
if (result != FG_OK) {
    // handle error ...
}

Working with Applet Parameters#

Once the frame grabber is initialized using an applet, the parameters of the applet can be read or manipulated. For example, to acquire images from a camera, the applet has to be configured to use the correct image dimensions and image format according to the image data the camera sends. Another example is configuring the trigger module provided by the applet to match the application requirements.

Parameter Identifiers and Parameter Names#

int Fg_getParameterIdByName(
    Fg_Struct * fg,
    const char * name);

const char * Fg_getParameterNameById(
    Fg_Struct * fg,
    unsigned int id,
    unsigned int dma);

FgParamTypes Fg_getParameterTypeById(
    Fg_Struct * fg,
    unsigned int id,
    unsigned int dma);

Info

The function Fg_getParameterTypeById() was added in version 5.9 of the Runtime SDK.

Each parameter of the applet is identified by its name, but the API uses a numerical identifier to access the parameter value or properties. Many parameters have fixed numerical identifiers as specified in the header file fgrab_define.h, however the use is not recommended for all but the most common parameters. To get the numerical identifier for a parameter, the function Fg_getParameterIdByName() is used. The function returns 0 or a negative error code if an error occurred translating the name into an identifier.

To get the name of a parameter for which the numerical identifier is known, the function Fg_getParameterNameById() can be used.

To get the type of a parameter for which the numerical identifier is known, the function Fg_getParameterTypeById() can be used.

Parameter Types#

Parameters are typed and to get or set the value of a parameter the type must be known, either from the documentation of the applet, implicitly by applets designed in-house using Visual Applets, or by requesting the type by using the function Fg_getParameterTypeById().

Parameter Type C/C++ Type
FG_PARAM_TYPE_INT32_T int32_t
FG_PARAM_TYPE_UINT32_T uint32_t
FG_PARAM_TYPE_INT64_T int64_t
FG_PARAM_TYPE_UINT64_T uint64_t
FG_PARAM_TYPE_DOUBLE double
FG_PARAM_TYPE_CHAR_PTR string
FG_PARAM_TYPE_SIZE_T size_t
FG_PARAM_TYPE_STRUCT_FIELDPARAMINT FieldParameterAccess
FG_PARAM_TYPE_STRUCT_FIELDPARAMINT64 FieldParameterAccess
FG_PARAM_TYPE_STRUCT_FIELDPARAMDOUBLE FieldParameterAccess

Parameters with one of the field parameter types should be handled using the FG_PARAM_TYPE_STRUCT_FIELDPARAMACCESS API described in subsection Accessing Field Parameters of chapter Considerations When Using Plain C.

Accessing Parameter Values#

int Fg_getParameterWithType(
    Fg_Struct * fg,
    int id,
    int32_t * value,
    unsigned int dma);

// Fg_getParameterWithType() is overloaded for:
//     int32_t * value
//     uint32_t * value
//     int64_t * value
//     uint64_t * value
//     float * value
//     double * value
//     std::string & value
int Fg_setParameterWithType(
    Fg_Struct * fg,
    int id,
    int32_t value,
    unsigned int dma);

// Fg_setParameterWithType() is overloaded for:
//     int32_t value
//     uint32_t value
//     int64_t value
//     uint64_t value
//     float value
//     double value
//     const std::string & value

To get or set the value of a parameter, the overloaded functions Fg_getParameterWithType() and Fg_setParameterWithType() should be used.

For example, given a frame grabber handle fg from a previous call to Fg_Init(), the following code sets the width of the first DMA channel of the applet to 1024.

const int dma = 0;
const int width = 1024;
int result = FG_INVALID_PARAMETER;

int paramId = Fg_getParameterIdByName(fg, "FG_WIDTH");
if (paramId > 0) {
    result = Fg_setParameterWithType(fg, paramId, width, dma);
}

The last parameter which is passed to the functions Fg_getParameterWithType() and Fg_setParameterWithType() in most cases refers to a DMA channel. Parameters like FG_WIDTH, FG_HEIGHT can be different for each DMA channel and each instance of these parameters can be requested or changed by passing the number of the DMA channel to the functions.

In some cases, the last parameter can refer to other logical indices. For example, the parameters FG_NR_OF_DMAS or FG_NR_OF_CAMS exist in multiple instances for each process of the applet, which is a scope defined in Visual Applets.

Other parameters are global in the context of an applet and the last parameter in the functions is ignored. One such parameter would be FG_NR_OF_PROCESSES. But even more prominently, if the application developer designs their own applets using Visual Applets, all parameters which control the applet can be considered global as they are identified by unique names, and the last parameter in the functions is ignored.

Some parameters can be different for every image acquired and require both the DMA channel and the frame number or buffer number. This applies to any image meta data, like the time stamp when an image has been transferred to the computer memory, the length of the actual image data, and similar information. These parameters cannot be handled by the functions mentioned above, and this topic will be discussed in more detail in chapter Image Acquisition.

Accessing Parameter Properties#

int Fg_getParameterPropertyWithType(
    Fg_Struct * fg,
    int id,
    FgProperty propertyId,
    int32_t * value);

// Fg_getParameterPropertyWithType() is overloaded for:
//     int32_t * value
//     uint32_t * value
//     int64_t * value
//     uint64_t * value
//     float * value
//     double * value
//     std::string & value
int Fg_getParameterPropertyWithTypeEx(
    Fg_Struct * fg,
    int id,
    FgProperty propertyId,
    int32_t * value,
    unsigned int dma);

// Fg_getParameterPropertyWithTypeEx() is overloaded for:
//     int32_t * value
//     uint32_t * value
//     int64_t * value
//     uint64_t * value
//     float * value
//     double * value
//     std::string & value

Info

The functions documented in this chapter were added in version 5.9 of the Runtime SDK. See section Accessing Parameter Properties in Plain C for the old interface.

Parameters have various properties besides the current value. Using the overloaded functions Fg_getParameterPropertyWithType() is not recommended, as the function calls implicitly use DMA channel 0, parameter properties might differ though for different channels. Using the overloaded functions Fg_getParameterPropertyWithTypeEx() the following properties can be requested:

Property Description Type
PROP_ID_VALUE Current parameter value same as parameter
PROP_ID_DATATYPE Parameter type
(enum value according to FgParamTypes)
int32_t
PROP_ID_NAME Descriptive name string
PROP_ID_PARAMETERNAME Parameter name string
PROP_ID_VALUELLEN Length required to encode the value to a string int32_t
PROP_ID_ACCESS Access flags of the parameter int32_t
PROP_ID_MIN Minimum parameter valueb same as parameter
PROP_ID_MAX Maximum parameter valueb same as parameter
PROP_ID_STEP Step size of the parameterb same as parameter
PROP_ID_IS_ENUM 0: not an enumeration parameter
n: size of buffer needed for PROP_ID_ENUM_VALUES
int32_t
PROP_ID_ENUM_VALUES Enumeration values (see Accessing the Enum Values Parameter Property) FgPropertyEnumValues[]
PROP_ID_FIELD_SIZE Number of elements in a field parameter int32_t

The list above is not complete and contains only the properties for the information which is not considered deprecated. See the SDK reference for more use cases of the API.

The following example shows how to get the minimum value of a parameter, assuming paramId is a parameter of type FG_PARAM_TYPE_INT32_T:

int minVal = 0;
int result =
    Fg_getParameterPropertyWithTypeEx(fg, paramId, PROP_ID_MIN,
                                      &minVal, dma);
if (result == FG_OK) {
    // work with the property ...
}

Memory Management#

To acquire images from a camera, memory is needed in the computer to transfer those images to and to access the image data from within a program. The Runtime SDK uses a circular buffer memory model. A memory buffer consists of at least two items, often referred to as frame buffers or sub-buffers, which will be re-used for subsequent frames acquired from the camera. How many frame buffers should be used depends on various factors, the most important one being how many further frames can arrive while a frame is still being processed. This will be discussed in more detail in section Acquisition Models of chapter Image Acquisition.

Each frame buffer must be of the same size, and must be large enough to store the largest possible image for the application requirements, usually defined by the width FG_WIDTH, height FG_HEIGHT and pixel format FG_FORMAT. (For an applet designed in Visual Applets, the width, height and pixel format will usually be more complex to setup.)

The circular buffer can be one large block of memory, contiguous in virtual address space and subdivided into equally sized frame buffers. Or it can consist of frame buffers which were individually allocated in memory and added to the management structure dma_mem used by the API.

void * Fg_AllocMem(
    Fg_Struct * fg,
    size_t totalSize,
    frameindex_t numFrames,
    unsigned int dma);

int Fg_FreeMem(
    Fg_Struct * fg,
    unsigned int dma);

There are three different sets of functions which can be used for memory management. However, using the functions Fg_AllocMem() and Fg_FreeMem() is not recommended as it is limited to the use of the standard acquisition model (ACQ_STANDARD). The remaining two sets of functions will be discussed here in more detail as they can be used with all three acquisition models (ACQ_STANDARD, ACQ_BLOCK and ACQ_SELECT) and can be applied both to simple use cases as well as to more specific requirements of the application.

Advanced Memory Management#

dma_mem * Fg_AllocMemEx(
    Fg_Struct * fg,
    size_t totalSize,
    frameindex_t numFrames);

int Fg_FreeMemEx(
    Fg_Struct * fg,
    dma_mem * mem);

The function Fg_AllocMemEx() can be used to allocate one large contiguous memory buffer of size totalSize which is subdivided into numFrames equally sized frame buffers. The function returns a handle to the memory management structure, or nullptr in case of an error.

The pointer returned is not a pointer to the memory buffer itself and should not be used directly! After the memory is no longer needed it should be released using Fg_FreeMemEx().

In the following example, the code will allocate a memory buffer for 16 frame buffers which can hold 24-bit RGB images of a size of 1024 x 1024:

const int width = 1024, height = 1024;
const int bytesPerPixel = 3;
const frameindex_t numFrames = 16;
const size_t frameSize = static_cast<size_t>(width) * height * bytesPerPixel;
const size_t totalSize = frameSize * numFrames;

dma_mem * mem = Fg_AllocMemEx(fg, totalSize, numFrames);
if (mem != nullptr) {
    // use memory, acquire and process images ...

    Fg_FreeMem(fg, mem);
}

The static_cast<size_t>(width) in the calculation of the frameSize. This is necessary to make sure that the size of very large images will be calculated correctly.

Flexible Memory Management#

dma_mem * Fg_AllocMemHead(
    Fg_Struct * fg,
    size_t totalSize,
    frameindex_t numFrames);

int Fg_AddMem(
    Fg_Struct * fg,
    void * frameBuffer,
    size_t size,
    frameindex_t index,
    dma_mem * mem);

int Fg_DelMem(
    Fg_Struct * fg,
    dma_mem * mem,
    frameindex_t index);

int Fg_FreeMemHead(
    Fg_Struct * fg,
    dma_mem * mem);

If the application has specific requirements for memory allocation instead of letting the SDK handle it, the function Fg_AllocMemHead() can be used to prepare the memory management structure. The function expects the same parameters as Fg_AllocMemEx(), but it will not allocate any memory. After allocating memory in the application, each frame buffer needs to be added individually using the function Fg_AddMem(). If the application requires dynamic changes of the frame buffers used for a memory buffer during acquisition runs, the function Fg_DelMem() can be used to remove frame buffers from the memory buffer. After the memory buffer is no longer needed, the management structure should be released first by calling Fg_FreeMemHead() before releasing the memory for the frame buffers.

The following code shows, how the function 'Fg_AllocMemEx()' really is just a convenience wrapper for the more flexible memory management API:

int Fg_AllocMemEx(Fg_Struct * fg, size_t totalSize, frameindex_t numFrames)
{
    const size_t frameSize = totalSize / numFrames;
    char * buf = nullptr;

    dma_mem * mem = Fg_AllocMemHead(fg, totalSize, numFrames);
    if (mem != nullptr) {
        try {
            buf = new char[totalSize];
            for (frameindex_t frame = 0; frame < numFrames; ++frame) {
                const int result =
                    Fg_AddMem(fg, buf + frameSize*frame,
                              frameSize, frame, mem);
                if (result != FG_OK) {
                    throw std::runtime_error("Failed to add frame buffer");
                }
            }
        }
        catch (...) {
            for (frameindex_t frame = 0; frame < numFrames; ++frame) {
                Fg_DelMem(fg, mem, frame);
            }
            Fg_FreeMemHead(fg, mem);
            delete[] buf;
            return nullptr;
        }
    }
    return mem;
}

Image Acquisition#

The Runtime SDK provides two modes of image data delivery which can be combined with three different acquisition models.

Image data can be delivered in synchronous or asynchronous mode. In the synchronous mode, an acquisition loop has to be provided by the application. In the asynchronous mode, a callback function can be registered which will be called whenever new image data is available. The runtime will provide the image acquisition loop in a separate thread for asynchronous mode. For some GUI frameworks, synchronizing the runtime thread context back into the GUI thread context can be complicated, and running an acquisition loop in a thread context provided by the framework might prove more sensible.

Registering a Callback Function for Asynchronous Mode#

typedef int (* Fg_ApcFunc_t)(
    frameindex_t frame,
    void * data);

int Fg_registerApcHandlerEx(
    Fg_Struct * fg,
    unsigned int dma,
    Fg_ApcFunc_t func,
    void * data,
    unsigned int timeout,
    unsigned int flags);

int Fg_unregisterApcHandler(
    Fg_Struct * fg,
    unsigned int dma);

Info

In version 5.9 of the Runtime SDK the functions Fg_registerApcHandlerEx() and Fg_unregisterApcHandler() were added and the behavior when using the blocking acquisition model ACQ_BLOCK was changed. See section Registering a Callback Function for Asynchronous Mode in Plain C for the old interface.

The function Fg_registerApcHandlerEx() can be used to setup the asynchronous mode before starting the acquisition.

Using Fg_registerApcHandlerEx(), a function of type Fg_ApcFunc_t can be registered to be called when image data is received. The function will be registered for a given frame grabber and DMA channel and will be passed two parameters when called. The first parameter to the callback function is a frame number (in ACQ_STANDARD and ACQ_SELECT) or buffer number (in ACQ_BLOCK) for the image received. The second parameter is a pointer which was supplied with the call to Fg_registerApcHandlerEx() and which can be used as a pointer to a context structure or class, for example the this pointer of a class instance which implements image processing.

Only one callback function can be registered for each DMA channel on any given frame grabber.

The callback function will be called from the acquisition loop provided by the runtime. This implies that callback function is called in the thread context of the acquisition loop. It also implies that the time the callback function takes until it returns is added to the general management overhead of the acquisition loop, and that during this time multiple images might have been received. If any images were received during that time, the callback function will be called again immediately after it returns.

The handling of the image acquisition can be controlled through the timeout and flags parameters in the call to Fg_registerApcHandlerEx(). The timeout is given in seconds which the acquisition loop should wait for new images to arrive. For the parameter flags, the following values and combinations thereof combined with the operator | (binary OR) can be used:

Flag Description
FG_APC_DEFAULTS default handling in the acquisition loop
FG_APC_BATCH_FRAMES only the newest image may be delivered in a single call to the callback function if multiple images were received
FG_APC_DELIVER_ERRORS deliver errors to callback function as negative frameindex_t values
FG_APC_IGNORE_TIMEOUTS ignore image timeouts and continue processing
FG_APC_IGNORE_APCFUNC_RETURN ignore the callback return value and continue processing
FG_APC_IGNORE_STOP ignore when the acquisition is stopped and continue processing
FG_APC_HIGH_PRIORITY increase the priority of the thread implementing the acquisition loop
FG_APC_OLD_ACQ_BLOCK_BEHAVIOR use Fg_getLastPicNumberBlockingEx() in ACQ_BLOCKc

The default handling for the acquisition loop is:

  • Every single image is delivered to the callback function
  • Error values are not delivered to the callback function
  • An image timeout ends the acquisition loop
  • A non-zero return value from the callback function ends the acquisition loop
  • Stopping the acquisition ends the acquisition loop
  • The acquisition loop runs in a thread with the default priority
  • The function Fg_getLastPicNumberBlockingEx() is called, and the result is delivered to the callback function

Info

Ending the acquisition loop will unregister the callback function automatically. While it may seem logical to always end the acquisition loop when the acquisition is stopped, this is not necessarily the case. Consider for example, if the application requires to run the acquisition in bursts of a certain number of images by calling Fg_AcquireEx with a finite number of frames to acquire. After the number of frames is acquired, the acquisition is automatically stopped by the driver. It might be convenient to keep the acquisition loop running and start acquisition bursts from a separate thread.

The following example shows how to register a callback function using a simple structure which holds information necessary for handling image acquisition:

struct ApcUserCallbackData
{
    Fg_Struct * fg;
    dma_mem * mem;
    unsigned int dma;
    unsigned int timeoutInSeconds;
    int mode;
};

int ApcUserCallback(frameindex_t frame, void * data)
{
    auto context = reinterpret_cast<ApcUserCallbackData *>(data);

    if (frame > 0) {
        // process new image ...
    } else {
        // handle error ...
    }

    return 0;
}

void SetupApcUserCallback(ApcUserCallbackData * context)
{
    // register callback function
    int result =
        Fg_registerApcHandlerEx(
            context->fg,
            context->dma,
            &ApcUserCallback,
            context,
            context->timeoutInSeconds,
            FG_APC_DELIVER_ERRORS | FG_APC_IGNORE_TIMEOUTS);
    if (result != FG_OK) {
        throw std::runtime_error("Failed to register callback function");
    }
}

The allocation and management of the context structure is not shown in the example. The pointer has to be valid while the callback function remains registered. One solution can be to keep everything together in a C++ class. To use a callback function in a C++ class context, a static function can be used to register the callback handler, and the this pointer should be used as context data pointer which can be casted back to a class pointer and used accordingly.

After the callback function is no longer needed, it can be unregistered by calling Fg_unregisterApcHandler() using the same frame grabber handle and DMA channel:

Fg_unregisterApcHandler(context->fg, context->dma);

Starting the Acquisition#

To enable transferring image data from the frame grabber to the application memory, the acquisition in the applet has to be started. This will inform the frame grabber about the frame buffers into which image data should be transferred and enable sending interrupts to the driver whenever the data for a new image has been successfully transferred. In most cases after starting the acquisition on the side of the frame grabber, the application will need to start the acquisition on the side of the camera and start triggering the camera.

See The Camera Control Library siso_genicam or The CameraLink Serial Interface Library clsersis for further information about controlling a camera. While triggering the camera is beyond the scope of this documentation, the documentation for the applet will give more information about the options the application has for triggering the camera through software or hardware signals to the frame grabber.

int Fg_Acquire(
    Fg_Struct * fg,
    unsigned int dma,
    frameindex_t frames);

int Fg_stopAcquire(
    Fg_Struct * fg,
    unsigned int dma);

The functions Fg_Acquire() and Fg_stopAcquire() can be used only in combination with the memory management functions Fg_AllocMem() and Fg_FreeMem() and are limited to using the standard acquisition model ACQ_STANDARD. Using these functions is not recommended and will not be documented. Instead the functions Fg_AcquireEx() and Fg_stopAcquireEx() should be used.

Image Acquisition Using Advanced or Flexible Memory Management#
int Fg_AcquireEx(
    Fg_Struct * fg,
    unsigned int dma,
    frameindex_t frames,
    int flags,
    dma_mem * mem);

int Fg_stopAcquireEx(
    Fg_Struct * fg,
    unsigned int dma,
    dma_mem * mem,
    int flags);

The function Fg_AcquireEx() can be called to start the image acquisition on a single DMA channel on a frame grabber. The acquisition will run until the number of frames given through the parameter frames is reached. If GRAB_INFINITE is passed to frames, the acquisition will run indefinitely. Through the parameter flags, the acquisition model can be selected. See section Acquisition Models for more information.

Model Description
ACQ_STANDARD continuous acquisition with no frame buffer protection
ACQ_BLOCK continuous acquisition with frame buffer blocking
ACQ_SELECT fully application controlled acquisition with manual frame buffer handling

When specifying a number of frames to acquire in the call to Fg_AcquireEx(), the acquisition will be stopped automatically when the runtime encounters the expected number of frames. In some cases this can lead to unexpected behavior when processing the last frame. To avoid this, in addition to the acquisition model, ACQ_NO_AUTOSTOP can be specified in the parameter flags. When ACQ_NO_AUTOSTOP is used, the driver will stop acquiring frames when the requested number of frames is reached, but the rest of the runtime and the frame grabber remain in acquisition mode until Fg_stopAcquireEx() is called.

To stop the acquisition and to reset the running state of the applet used, the function Fg_stopAcquireEx() should be called. Through the parameter flags, the stop mode can be selected:

Mode Description
STOP_ASYNC stop the acquisition immediately
STOP_SYNC_TO_APC synchronize stopping the acquisition in asynchronous mode to the callback function
the time to wait for the callback to finish in milliseconds can be set using the parameter FG_APC_STOP_TIMEOUT
STOP_SYNC synchronize stopping the acquisition with the driver
the time to wait for the next image to finish transferring in seconds can be set using the parameter FG_STOP_TIMEOUT
STOP_ASYNC_FALLBACK can be used together with STOP_SYNC
if the stop could not be synchronized, fall back to STOP_ASYNC

Writing an Acquisition Loop for Synchronous Mode#

In synchronous mode, the application has to handle receiving new images. This is usually done in the form of an acquisition loop which calls one of the functions provided by the Runtime SDK which wait for new images to be transferred on a specific DMA channel. To understand which function to use, see section Acquisition Models for more information. For each model, an example acquisition loop is given in the corresponding subsection.

frameindex_t Fg_getLastPicNumberBlocking(
    Fg_Struct * fg,
    frameindex_t frame,
    unsigned int dma,
    int timeout);

The function Fg_getLastPicNumberBlocking() can be used only in combination with the function Fg_Acquire() and is limited to the standard acquisition model ACQ_STANDARD. Using this function is not recommended and will not be documented. Instead the functions Fg_getLastPicNumberBlockingEx() or Fg_getImageEx() should be used.

Waiting for Images Using Advanced or Flexible Memory Management#
frameindex_t Fg_getLastPicNumberBlockingEx(
    Fg_Struct * fg,
    frameindex_t frame,
    unsigned int dma,
    int timeout,
    dma_mem * mem);

frameindex_t Fg_getImageEx(
    Fg_Struct * fg,
    int strategy,
    frameindex_t frame,
    unsigned int dma,
    unsigned int timeout,
    dma_mem * mem);

The function Fg_getLastPicNumberBlockingEx() waits for the frame requested in the parameter frame to arrive within the number of seconds given in the parameter timeout on a given DMA channel as specified in the parameter dma. The first frame is numbered 1, not 0. The function returns a frame number larger than 0 in case of success, or a negative error code in case of a failure.

Info

The frame number returned can be larger than the frame number which was requested, as the function will always return the newest frame number available. This means that between two calls to the function, more than one frame might have arrived and the application needs to decide if only the newest frame or all frames should be processed.

The following example shows a simple acquisition loop for ACQ_STANDARD:

const int timeoutInSeconds = 10;

frameindex_t nextFrame = 1;
while (true) {
    // get new image
    const frameindex_t newestFrame =
        Fg_getLastPicNumberBlockingEx(fg, nextFrame, dma,
                                      timeoutInSeconds, mem);

    if (newestFrame > 0) {
        // process new image(s) ...

        nextFrame = (newestFrame < FRAMEINDEX_MAX) ? newestFrame + 1 : 1;
    } else {
        // handle error ...
    }
}

The function Fg_getImageEx() is much more complex. The behavior of the function and the meaning of the return code in case of success depend on the parameters strategy and timeout and on the acquisition model in use. In case of an error, the result will always be a negative error code. A short description of the different strategies will be given below, but generally the use of the function should be limited to the use cases described in section The Blocking Acquisition Model. Any other use of the function is discouraged and will not be discussed in further detail in the context of this document.

SEL_NEW_IMAGE: The function requires a timeout to be specified and waits for at least one new image to arrive. In ACQ_STANDARD and ACQ_SELECT the frame number of the newest image is returned. In ACQ_BLOCK, the function blocks the frame buffer for the newest image, unblocks any frame buffers of additional images, and returns the buffer number of the blocked image.

SEL_NEXT_IMAGE: In ACQ_STANDARD, if no timeout is specified, the function will return the buffer number of the newest image received. If a timeout is specified, the function will wait for the next frame after the last one which was received in a previous call to the function, or frame number 1 if the function was not called previously. The function will return the frame number of the newest image received. In ACQ_BLOCK, the function blocks the frame buffer of the first image received which has not been blocked or unblocked yet and returns the buffer number of the blocked image. If no more images can be blocked and a timeout was specified, the function waits for at least one new image to arrive before performing the blocking operation.

SEL_ACT_IMAGE: In ACQ_STANDARD, the function behaves the same was as when SEL_NEXT_IMAGE is specified. In ACQ_BLOCK, the function blocks the frame buffer for the newest image, unblocks any frame buffers of additional images, and returns the buffer number of the blocked image. If no more images can be blocked and a timeout was specified, the function waits for at least one new image to arrive before performing the blocking operation.

SEL_LAST_IMAGE: The function returns the last value returned by the function, or by Fg_getLastPicNumberBlockingEx(). This is either the last frame number, or buffer number, depending on how the function was called last time.

SEL_NUMBER: The function mimics the behavior of Fg_getLastPicNumberBlockingEx().

The following example shows a simple acquisition loop for ACQ_BLOCK using SEL_NEXT_IMAGE (every image acquired is processed):

const int timeoutInSeconds = 10;

while (true) {
    // get new image
    const frameindex_t buffer =
        Fg_getImageEx(fg, SEL_NEXT_IMAGE, 0, dma, timeoutInSeconds, mem);

    if (buffer > 0) {
        // process new image ...
    } else {
        // handle error ...
    }
}

The Driver Image Acquisition Timeout FG_TIMEOUT#

In addition to specifying a time to wait for images when calling the functions Fg_getImageEx() or Fg_getLastPicNumberBlockingEx(), or when setting up a callback function for the asynchronous mode, the driver also keeps track of the image acquisition in the context of handling image transfer interrupts. The driver will stop the image acquisition if no image data is received during the number of seconds specified in the parameter FG_TIMEOUT. If FG_TIMEOUT is set to FG_TIMEOUT_INFINITE (the value of FG_TIMEOUT_INFINITE is INT_MAX - 1) the driver will not stop the acquisition regardless of the time span between receiving two images.

The value of the parameter FG_TIMEOUT is forwarded to the driver when the acquisition is started. Any change to the parameter after the start of the acquisition will not be taken into account by the driver until the acquisition is stopped and started again.

After the driver has stopped the acquisition, any function call still waiting for images will return FG_TIMEOUT_ERR and any function called to wait for images after the acquisition was stopped will return FG_TRANSFER_NOT_ACTIVE.

Info

The default value for the parameter FG_TIMEOUT is not FG_TIMEOUT_INFINITE, but in most cases is set to 1000000 seconds (this equals about 277 hours and 46 minutes). It is recommended to explicitly set the parameter FG_TIMEOUT to FG_TIMEOUT_INFINITE before starting the acquisition.

The following example shows how to disable the driver image acquisition timeout:

Fg_setParameterWithType(fg, FG_TIMEOUT, FG_TIMEOUT_INFINITE, dma);

Frame and Acquisition Information#

int Fg_getParameterEx(
    Fg_Struct * fg,
    int param,
    void * value,
    unsigned int dma,
    dma_mem * mem,
    frameindex_t frame);

void * Fg_getImagePtrEx(
    Fg_Struct * fg,
    frameindex_t frame,
    unsigned int dma,
    dma_mem * mem);

frameindex_t Fg_getStatusEx(
    Fg_Struct * fg,
    int status,
    frameindex_t frame,
    unsigned int dma,
    dma_mem * mem);

For each frame received, the following information can be requested by calling the function Fg_getParameterEx():

Parameter Description Type
FG_TRANSFER_LEN The actual number of bytes transferred size_t
FG_TIMESTAMP_LONG High resolution timestamp of the image uint64_t
FG_TIMESTAMP_LONG_FREQUENCY High resolution timestamp frequency uint64_t
FG_TIMESTAMP Timestamp of the image in milliseconds uint32_t
FG_IMAGE_TAG Image tag uint32_t
FG_IMAGE_NUMBER Frame number uint64_t

Info

The frame number information was added in version 5.9 of the Runtime SDK.

The information for each frame is not available indefinitely, but is only valid as long as the corresponding buffer has not been queued for subsequent transfers. Depending on the acquisition model used, the function requires a frame number or a buffer number. See section Acquisition Models for more details.

The following example shows how to request the timestamp for a given frame:

// get time stamp frequency
uint64_t frequency = 0;
Fg_getParameterEx(fg, FG_TIMESTAMP_LONG_FREQUENCY, &frequency, 0, nullptr, 0);

// ...

// get frame time stamp
uint64_t timestamp = 0
int result =
    Fg_getParameterEx(fg, FG_TIMESTAMP_LONG, &timestamp, dma, mem, frame);
if (result == FG_OK) {
    // this will probably be 'seconds since booting the computer' ...
    // it makes more sense when you calculate the difference
    // between timestamps of two frames
    double seconds =
        static_cast<double>(timestamp)/static_cast<double>(frequency);

    // ...
}

To get a pointer to a frame buffer, the function Fg_getImagePtrEx() can be called. The pointer for each frame is not available indefinitely, but is only valid as long as the corresponding buffer has not been queued for subsequent transfers. Depending on the acquisition model used, the function requires a frame number or a buffer number. See section Acquisition Models for more details.

The following example shows how to request the frame buffer pointer for a given frame:

// get a pointer to the frame buffer for the newest image
void * buffer = Fg_getImagePtrEx(fg, frame, dma, mem);

// get the actual number of bytes transferred
size_t length = 0;
int result =
    Fg_getParameterEx(fg, FG_TRANSFER_LEN, &length, dma, mem, frame);

if ((buffer != nullptr) && (result == FG_OK) && (length > 0)) {
        // process image data ...
}

The function Fg_getStatusEx() can be called to request the following general status information, the parameter frame will be ignored for these:

Status Description
NUMBER_OF_GRABBED_IMAGES the total number of frames transferred
NUMBER_OF_LAST_IMAGE the last frame number reported by Fg_getLastPicNumberBlockingEx()
NUMBER_OF_NEXT_IMAGE the frame number of the next frame after the last one reported
GRAB_ACTIVE 0: the DMA channel is not active
1: the DMA channel is active

The following example shows how to request the acquisition status for a DMA channel:

frameindex_t active =
    Fg_getStatusEx(fg, GRAB_ACTIVE, 0, dma, mem);
if (active == 1) {
    // the DMA channel is active ...
} else if (active == 0) {
    // the DMA channel is inactive ...
} else {
    // handle error ...
}

Acquisition Models#

The Runtime SDK provides three different acquisition models, which can be selected when calling Fg_AcquireEx(): standard, blocking and selective. All three acquisition models are based on the memory model explained in chapter Memory Management which uses at least two frame buffers for acquiring images.

For the examples in this section, a memory buffer which consists of four frame buffers contiguous in virtual memory will be used.

Memory Buffer Model

Frame Numbers and Buffer Numbers#

Throughout the Runtime SDK, frame numbers and buffer numbers are used, both of which are of type frameindex_t.

While frame numbers are strictly monotonic increasing natural numbers in the range of [1; FRAMEINDEX_MAX], referring to the number of images acquired since the start of the acquisition, the type frameindex_t used for frame numbers is a signed type and is used to return negative error codes as well as frame numbers every so often in the API. Especially in 32-bit applications, care must be taken to handle the overflow of the frame number correctly: once FRAMEINDEX_MAX has been reached, image counting will wrap to 1! (In 64-bit applications even with extremely high frame rates, overflow of the image counter will happen only after hundreds of thousands of years.)

The buffer number in contrast refers to the frame buffer in which image data resides, and for N buffers is in the range of [1; N]. In the examples used here, the buffer number is between 1 … 4 and refers to FB0FB3 accordingly.

The Standard Acquisition Model (ACQ_STANDARD)#
frameindex_t Fg_getLastPicNumberBlockingEx(
    Fg_Struct * fg,
    frameindex_t frame,
    unsigned int dma,
    int timeout,
    dma_mem * mem);
int Fg_getParameterEx(
    Fg_Struct * fg,
    int param,
    void * value,
    unsigned int dma,
    dma_mem * mem,
    frameindex_t frame);

void * Fg_getImagePtrEx(
    Fg_Struct * fg,
    frameindex_t frame,
    unsigned int dma,
    dma_mem * mem);

The standard acquisition model is selected by passing ACQ_STANDARD to flags in the call to Fg_AcquireEx(). All frame buffers are always accessible both by the driver for acquiring image data and by the application to process image data.

When acquisition is started, the driver queues at least one frame buffer in the frame grabber for image data transfer, starting with the first frame buffer FB0. Once an image has been transferred completely to the computer memory, the software is informed of the completed transfer, and the driver will queue one more frame buffer. The frame grabber will continue data transfer for the next image into the next buffer in the queue, in our example FB1.

(The number of frame buffers queued depends on various factors, like the frame grabber used, the driver version, and settings in the Windows registry or parameters to the Linux driver. However, in standard acquisition mode, queueing will always be in a linear manner and will start with FB0 again after the last frame buffer has been queued. While the number of buffers queued can have an effect on the maximum frame rate which can be achieved in a computer, this effect will only be measurable above ~10.000 frames per second.)

Because the driver simply uses frame buffers in a consistent circular manner, frame numbers can be naively translated to buffer numbers using the relation bufferNumber = 1 + ((frameNumber - 1) % numberOfBuffers). The function Fg_getImagePtrEx() can be used to get a pointer to the frame buffer for either a frame number, or a buffer number.

In the standard acquisition model the application is fully responsible for ensuring that image acquisition will not "overrun" image processing. This can only be ensured if triggering the image source is controlled from the application in combination with the image processing, or in which the number of buffers are carefully tuned to the frame rate and the performance of the computer system used, or in which images can be skipped.

In a scenario, where the application triggers only a single image while a previous image is still being processed, two buffers are sufficient, one for the image data being processed and one for transferring image data to. (If the application would make sure that triggers are only generated after an image has been completely processed, actually one buffer would be sufficient for processing. This is however not supported by the Runtime SDK, and the application has to allocate two buffers at least in all cases.)

In a scenario, where images are generated continuously without control from the application side, both the image source and the buffer handling in the driver can be considered free running. In the following example, FB0 and FB1 contain valid image data. FB0 is being processed by the application, FB1 is pending processing. The driver has queued at least FB2 and image data is currently being transferred. This situation is still safe, because FB3 is not in use yet, even though it might be queued already.

Memory Buffer Model Safe

Calling the function Fg_getLastPicNumberBlockingEx() for the next frame number after processing FB0 is finished would return a single new frame (the frame number for the image transferred to FB1) in this case if the transfer to FB2 has not finished in the meantime.

If image processing is slower than image acquisition, the application might have moved on to process FB1. However, in the following example in the meantime two more images were transferred, FB2 and FB3, and the driver has started again with FB0. As soon as the image transfer completes, the driver will use FB1, and thus the frame grabber might overwrite data which is currently being processed. This scenario is only safe, if the application can make sure that no image data is being generated by the image source while FB1 is still being processed.

Memory Buffer Model Unsafe

Calling the function Fg_getLastPicNumberBlockingEx() for the next frame number after processing FB0 is finished would return two new frames (the frame number for the image transferred to FB3) in this case if the transfer to FB0 has not finished in the meantime.

The following example extends the structure of the acquisition loop given in section Writing an Acquisition Loop to process all frames:

const int timeoutInSeconds = 10;

frameindex_t nextFrame = 1;
while (true) {
    // get new image
    const frameindex_t newestFrame =
        Fg_getLastPicNumberBlockingEx(fg, nextFrame, dma,
                                      timeoutInSeconds, mem);

    if (newestFrame > 0) {
        for (frameindex_t frame = nextFrame; frame <= newestFrame; ++frame) {
                // get a pointer to the frame buffer for the newest image
                void * ptr =
                    Fg_getImagePtrEx(fg, frame, dma, mem);

            // get the actual number of bytes transferred
            size_t length = 0;
            int result =
                Fg_getParameterEx(fg, FG_TRANSFER_LEN, &length,
                                  dma, mem, frame);

            if ((ptr != nullptr) && (result == FG_OK) && (length > 0)) {
                    // process image data ...
            }
        }

        nextFrame = (newestFrame < FRAMEINDEX_MAX) ? newestFrame + 1 : 1;
    } else {
        // handle error ...
    }
}

If the application can skip images and only the newest frame received should be processed, the inner for-loop can be dropped from the acquisition loop, instead processing only newestFrame.

The function Fg_getImageEx() should not be used in combination with the standard acquisition model.

In asynchronous mode, the runtime will call Fg_getLastPicNumberBlockingEx() to wait for a new image, and will call the callback function once for the newest image if the flag FG_APC_BATCH_FRAMES was set, or multiple times if it was not set, once for each image received. The callback will receive a frame number for the image.

To process frames in asynchronous mode, the content of the inner loop can be used in the callback function (ApcCallbackData is the structure as described in section Registering a Callback Function for Asynchronous Mode):

int ApcUserCallback(frameindex_t frame, void * data)
{
    auto context = reinterpret_cast<ApcUserCallbackData *>(data);

    if (frame > 0) {
        // get a pointer to the frame buffer for the newest image
        void * ptr =
            Fg_getImagePtrEx(context->fg, frame, context->dma, context->mem);

        // get the actual number of bytes transferred
        size_t length = 0;
        int result =
            Fg_getParameterEx(context->fg, FG_TRANSFER_LEN, &length,
                              context->dma, context->mem, frame);

        if ((ptr != nullptr) && (result == FG_OK) && (length > 0)) {
            // process image data ...
        }
    } else {
        // handle error ...
    }

    return 0;
}

This way, the callback function can be used both for processing only the newest image, when FG_APC_BATCH_FRAMES was passed in flags to Fg_registerApcHandlerEx(), as well as processing all images. The callback function will simply be called multiple times, once for each new image, when FG_APC_BATCH_FRAMES was not passed.

The Blocking Acquisition Model (ACQ_BLOCK)#
frameindex_t Fg_getImageEx(
    Fg_Struct * fg,
    int strategy,
    frameindex_t frame,
    unsigned int dma,
    unsigned int timeout,
    dma_mem * mem);
int Fg_getParameterEx(
    Fg_Struct * fg,
    int param,
    void * value,
    unsigned int dma,
    dma_mem * mem,
    frameindex_t buffer);

void * Fg_getImagePtrEx(
    Fg_Struct * fg,
    frameindex_t buffer,
    unsigned int dma,
    dma_mem * mem);

frameindex_t Fg_getStatusEx(
    Fg_Struct * fg,
    int status,
    frameindex_t buffer,
    unsigned int dma,
    dma_mem * mem);
int Fg_setStatusEx(
    Fg_Struct * fg,
    int status,
    frameindex_t buffer,
    unsigned int dma,
    dma_mem * mem);

Info

The behavior when using asynchronous mode in the blocking acquisition model ACQ_BLOCK was changed in version 5.9 of the Runtime SDK.

The blocking acquisition model is selected by passing ACQ_BLOCK to flags in the call to Fg_AcquireEx(). Each frame buffer is either exclusively accessible by the driver for acquiring image data or queued or blocked for the application to process image data. Using frame buffers is always safe as long as they are blocked, but images may be lost when the driver encounters a situation where no more unblocked buffers are available for image data transfer.

When acquisition is started, the driver queues at least one frame buffer in the frame grabber for image data transfer, starting with the first frame buffer FB0. Once an image has been transferred completely to the computer memory, the software is informed of the completed transfer, the frame buffer is queued for the application and the driver will not use it until the application has explicitly or implicitly unblocked it. The driver will queue one more frame buffer in the frame grabber as long as one or more unblocked frame buffers are available. If no more unblocked frame buffers are available, a special frame buffer called dummy buffer is queued in the frame grabber to keep image acquisition always running. The frame grabber will continue data transfer for the next image into the next buffer in the queue, in our example this would be FB1.

(As mentioned in subsection The Standard Acquisition Model, the number of frame buffers queued in the frame grabber depends on various factors.)

The dummy frame buffer can be considered infinitely large and can absorb data of arbitrary size, however not in a meaningful way for processing an image. Because of this, the dummy buffer is not accessible to the application, and any image transferred to the dummy buffer will be lost. Because queueing the next buffer happens automatically each time a frame is transferred, this means that automatically at least one image will be lost if no unblocked frame buffers are available to the driver for queueing. This is true even if the data source is stopped and started again only after a frame buffer becomes available again!

In synchronous mode, the function Fg_getImageEx() should be called to request and block one frame buffer for newly acquired images. The function will return a buffer number for the blocked frame buffer. The following strategies are relevant to the blocking acquisition model:

Strategy Description
SEL_NEXT_IMAGE returns the buffer number for the next frame
this strategy is used to process each single frame, one after the other in the order they were transferred
SEL_ACT_IMAGE returns the buffer number for the newest frame
this strategy is used to allow skipping frames when image processing is slower than the acquisition frame rate

The list above is not complete, and only contains the strategies relevant to the blocking acquisition model.

If SEL_NEXT_IMAGE is used, only the next frame buffer will be blocked, all others remain queued for the application to be requested at a later point. If SEL_ACT_IMAGE is used, only the newest frame buffer will be blocked, all other frame buffers which are queued for the application will be removed from the queue and unblocked implicitly.

When calling the function Fg_getImageEx() using this model, 0 should always be passed to frame. The function cannot be used to wait for a specific frame number or to translate frame numbers into buffer numbers. To get the frame number for a frame buffer, the function Fg_getParameterEx() can be called passing FG_IMAGE_NUMBER in the parameter param, a pointer to a variable of type frameindex_t for the frame number and the buffer number in the parameter buffer.

The function Fg_getStatusEx() can be called to request the following status information relevant for the blocking acquisition model:

Status Description
NUMBER_OF_LOST_IMAGES the number of frames lost
NUMBER_OF_BLOCKED_IMAGES the number of frames currently blocked
NUMBER_OF_IMAGES_IN_PROGRESS the number of frames available through Fg_getImageEx()
BUFFER_STATUS 0: the frame buffer is not blocked
1: the frame buffer is blocked

The function Fg_setStatusEx() can be called to unblock frame buffers after they were requested an blocked by a previous call to Fg_getImageEx():

Status Description
FG_UNBLOCK unblock a single frame buffer
FG_UNBLOCK_ALL unblock all currently blocked buffers

FG_UNBLOCK_ALL will also remove any buffers still queued for the application which have not yet been requested and blocked.

When using the function Fg_AcquireEx() to specify a number of images to be grabbed, lost images are considered as well as images delivered successfully. This may lead to unexpected FG_TIMEOUT_ERR or FG_TRANSFER_NOT_ACTIVE results when calling Fg_getImageEx() and only counting delivered images in the acquisition loop.

In a scenario, where the application triggers only a single image while a previous image is still being processed, two buffers are sufficient, one for the image data being processed and one for transferring image data to. (Due to buffer locking taking place, even if the application would make sure that triggers are only generated after an image has been completely processed, two buffers are required at least in all cases.)

In a scenario, where images are generated continuously without control from the application side, the image source can be considered free running. The driver however will only be able to deliver images to the application while there are still unblocked buffers available. In the following example, FB0 and FB1 contain valid image data. FB0 is being processed by the application, FB1 is pending processing. The driver has queued at least FB2 and image data is currently being transferred. This situation is still safe, because FB3 is not in use yet, even though it might be queued already.

Memory Buffer Model Safe

If image processing is slower than image acquisition, the application might have finished processing FB0, unblocked it and moved on to process FB1. However, in the following example in the meantime two more images were transferred, FB2 and FB3, and the driver has started again with FB0. As soon as the image transfer completes, if the application hasn't finished processing FB1 and unlocked it, the driver will have no more free buffers available for queueing. The dummy buffer will be queued, and at least one image will be lost in the acquisition, but data integrity of the frame buffers not yet unblocked will be kept.

Memory Buffer Model Unsafe

The following example extends the structure of the acquisition loop given in section Writing an Acquisition Loop to process all frames:

const int timeoutInSeconds = 10;
while (true) {
    // get new image
    frameindex_t buffer =
        Fg_getImageEx(fg, SEL_NEXT_IMAGE, 0, dma, timeoutInSeconds, mem);

    if (buffer > 0)
        // get the frame number (if needed)
        uint64_t frame = 0;
        Fg_getParameterEx(fg, FG_IMAGE_NUMBER, &frame, dma, mem, buffer);

        // get a pointer to the frame buffer for the newest image
        void * ptr = Fg_getImagePtrEx(fg, buffer, dma, mem);

        // get the actual number of bytes transferred
        size_t length = 0;
        int result =
            Fg_getParameterEx(fg, FG_TRANSFER_LEN, &length, dma, mem, buffer);

        if ((ptr != nullptr) && (result == FG_OK) && (length > 0)) {
            // process image data ...
        }

        // unblock frame buffer
        Fg_setStatusEx(fg, FG_UNBLOCK, buffer, dma, mem);
    } else {
        // handle error ...
    }
}

The function Fg_getLastPicNumberBlockingEx() should not be used in combination with the blocking acquisition model.

In asynchronous mode, the runtime will call Fg_getImageEx() using SEL_ACT_IMAGE if the flag FG_APC_BATCH_FRAMES was set, or using SEL_NEXT_IMAGE if it was not set. The callback function will receive a buffer number for the image.c

To process frames in asynchronous mode, the if-clause can be used in the callback function (ApcUserCallbackData is the structure as described in section Registering a Callback Function for Asynchronous Mode):

int ApcUserCallback(frameindex_t buffer, void * data)
{
    auto context = reinterpret_cast<ApcUserCallbackData *>(data);

    if (buffer > 0) {
        // get the frame number (if needed)
        uint64_t frame = 0;
        Fg_getParameterEx(fg, FG_IMAGE_NUMBER, &frame, dma, mem, buffer);

        // get a pointer to the frame buffer for the newest image
        void * ptr =
            Fg_getImagePtrEx(context->fg, buffer, context->dma, context->mem);

        // get the actual number of bytes transferred
        size_t length = 0;
        int result =
            Fg_getParameterEx(context->fg, FG_TRANSFER_LEN, &length,
                              context->dma, context->mem, buffer);

        if ((ptr != nullptr) && (result == FG_OK) && (length > 0)) {
            // process image data ...
        }

        // unblock frame buffer
        Fg_setStatusEx(fg, FG_UNBLOCK, buffer, dma, mem);
    } else {
        // handle error ...
    }

    return 0;
}

This way, the callback function can be used both for processing only the newest image, when FG_APC_BATCH_FRAMES was passed in flags to Fg_registerApcHandlerEx(), as well as processing all images. The callback function will simply be called multiple times, once for each new image, when FG_APC_BATCH_FRAMES was not passed.

The Selective Acquisition Model ACQ_SELECT#
frameindex_t Fg_getLastPicNumberBlockingEx(
    Fg_Struct * fg,
    frameindex_t frame,
    unsigned int dma,
    int timeout,
    dma_mem * mem);
int Fg_getParameterEx(
    Fg_Struct * fg,
    int param,
    void * value,
    unsigned int dma,
    dma_mem * mem,
    frameindex_t frame);

void * Fg_getImagePtrEx(
    Fg_Struct * fg,
    frameindex_t buffer,
    unsigned int dma,
    dma_mem * mem);
int Fg_setStatusEx(
    Fg_Struct * fg,
    int status,
    frameindex_t buffer,
    unsigned int dma,
    dma_mem * mem);

Info

The selective acquisition model ACQ_SELECT was added in version 5.9 of the Runtime SDK.

The selective acquisition model is selected by passing ACQ_SELECT to flags in the call to Fg_AcquireEx(). The application has full control over the use of frame buffers by the driver.

Frame buffers have to be selected explicitly for queueing in the frame grabber, and the driver will queue frame buffers in the exact order they were selected. When acquisition is started, the driver queues frame buffers in the frame grabber for image data transfer only if any frame buffers were selected, starting with the first frame buffer which was selected. Once an image has been transferred completely to the computer memory, the software is informed of the completed transfer, the frame buffer is not considered selected anymore and the driver will not use it until it is selected explicitly again. The driver will queue one more frame buffers if available. If no more frame buffers are available, simply no frame buffer will be queued. The frame grabber will continue data transfer for the next image into the next buffer in the queue. If the queue in the frame grabber runs empty this may result in an internal buffer overflow and images might be lost.

(As mentioned in subsection The Standard Acquisition Model, the number of frame buffers queued in the frame grabber depends on various factors.)

Because the application has full control over the use of frame buffers, keeping track of the buffer numbers is also the responsibility of the application. Unless frame buffers are always selected in the same order, there is no naive way to translate frame numbers to buffer numbers. Also there is no API function to perform the translation.

The function Fg_setStatusEx() can be called to select a frame buffer by using FG_SELECT_BUFFER and passing the buffer number.

To keep things simple, in the following examples we assume that after starting the acquisition all four buffers FB0FB3 are selected in natural order. Further, after an image is transferred it is processed and the buffer selected again in order. This way, the sequence of buffers will always remain the same, and frame numbers can be naively translated to buffer numbers using the relation bufferNumber = 1 + ((frameNumber - 1) % numberOfBuffers).

In a scenario, where images are generated continuously without control from the application side, the image source can be considered free running. The driver however will only be able to deliver images to the application while there are still selected buffers available. In the following example, FB0 and FB1 contain valid image data and are not selected. FB0 is being processed by the application, FB1 is pending processing. The driver has queued at least FB2 and image data is currently being transferred. This situation is still safe, because FB3 is not in use yet, even though it might be queued already.

Memory Buffer Model Safe

Calling the function Fg_getLastPicNumberBlockingEx() for the next frame number after processing FB0 is finished would return a single new frame (the frame number for the image transferred to FB1) in this case if the transfer to FB2 has not finished in the meantime.

If image processing is slower than image acquisition, the application might have finished processing FB0, selected it and moved on to process FB1. However, in the following example in the meantime two more images were transferred, FB2 and FB3, and the driver has started again with FB0. As soon as the image transfer completes, if the application hasn't finished processing FB1 and selected it, the driver will have no more free buffers available for queueing. As long as no further image arrives, this situation does not cause the same implicit image loss as would be encountered in ACQ_BLOCK. But as soon as another image is received, the internal memory buffers in the frame grabber can run into an overflow condition and data loss can occur.

Memory Buffer Model Unsafe

The following example extends the structure of the acquisition loop given in section Writing an Acquisition Loop to process all frames:

const frameindex_t numberOfBuffers = 4;
const int timeoutInSeconds = 10;

frameindex_t nextFrame = 1;
while (true) {
    // get new image
    const frameindex_t newestFrame =
        Fg_getLastPicNumberBlockingEx(fg, nextFrame, dma,
                                      timeoutInSeconds, mem);

    if (newestFrame > 0) {
        for (frameindex_t frame = nextFrame; frame <= newestFrame; ++frame) {
            // get the buffer number
            frameindex_t buffer = 1 + ((frame - 1) % numberOfBuffers);

            // get a pointer to the frame buffer for the newest image
            void * ptr = Fg_getImagePtrEx(fg, buffer, dma, mem);

            // get the actual number of bytes transferred
            size_t length = 0;
            int result =
                Fg_getParameterEx(fg, FG_TRANSFER_LEN, &length,
                                  dma, mem, buffer);

            if ((ptr != nullptr) && (result == FG_OK) && (length > 0)) {
                // process image data ...
            }

            // select frame buffer
            Fg_setStatusEx(fg, FG_SELECT_BUFFER, buffer, dma, mem);
        }

        nextFrame = (newestFrame < FRAMEINDEX_MAX) ? newestFrame + 1 : 1;
    } else {
        // handle error ...
    }
}

If the application can skip images and only the newest frame received should be processed, Fg_setStatusEx() has to be used with FG_SELECT_BUFFER to select buffers which were skipped. Otherwise the application will end up with unused buffers.

The function Fg_getImageEx() should not be used in combination with the selective acquisition model.

In asynchronous mode, the runtime will call Fg_getLastPicNumberBlockingEx() to wait for a new image, and will call the callback function once for the newest image if the flag FG_APC_BATCH_FRAMES was set, or multiple times if it was not set, once for each image received. The callback will receive a frame number for the image.

To process frames in asynchronous mode, the content of the inner loop can be used in the callback function (ApcUserCallbackData is the structure as described in section Registering a Callback Function for Asynchronous Mode):

int ApcUserCallback(frameindex_t frame, void * data)
{
    auto context = reinterpret_cast<ApcUserCallbackData *>(data);

    if (frame > 0) {
        // get the buffer number
        frameindex_t buffer = 1 + ((frame - 1) % numberOfBuffers);

        // get a pointer to the frame buffer for the newest image
        void * ptr = Fg_getImagePtrEx(fg, buffer, dma, mem);

        // get the actual number of bytes transferred
        size_t length = 0;
        int result =
            Fg_getParameterEx(fg, FG_TRANSFER_LEN, &length, dma, mem, buffer);

        if ((ptr != nullptr) && (result == FG_OK) && (length > 0)) {
            // process image data ...
        }

        // select frame buffer
        Fg_setStatusEx(fg, FG_SELECT_BUFFER, buffer, dma, mem);
    } else {
        // handle error ...
    }

    return 0;
}

This way, the callback function can be used both for processing only the newest image, when FG_APC_BATCH_FRAMES was passed in flags to Fg_registerApcHandlerEx(), as well as processing all images. The callback function will simply be called multiple times, once for each new image, when FG_APC_BATCH_FRAMES was not passed.

Applet Events#

The applets that are included in the runtime installation can notify the application about various events related to image acquisition or processing in the frame grabber. Examples of such events are start of frame events, or end of frame events which are generated whenever the first, or last, pixel of an image is received from the camera, or trigger input rising events, or trigger input falling events which are generated whenever a trigger input signal edge is detected. Users of Visual Applets can use event operators within their design to generate events as needed by their application.

Each event source has a unique name and is identified by a single bit of an unsigned 64-bit integer, called the event mask. This way, multiple event sources can be grouped together by setting multiple bits in an event mask, for example by using the binary or operator | for multiple event masks for several event sources. However, this also implies that any applet can support only up to 64 event sources.

Events can be delivered in synchronous or asynchronous mode. In the synchronous mode, an event loop has to be provided by the application. In the asynchronous mode, a callback function can be registered which will be called whenever one or more events occur. Both approaches should not be used mixed in an application as this can cause resource conflicts.

Since event sources can generate a high interrupt load on the computer system, each event source must be enabled explicitly, and should only be activated when needed.

For every occurrence of an event, the time when the interrupt for the event source was received is recorded. In addition to the time stamp, some event sources can generate additional data with each event, called the event payload. Events that have a payload cannot be grouped together by setting multiple bits for more than one event source. Consult the documentation for any applet you use to learn about whether or not event sources in an applet generate additional data, and how to interpret that data.

Event Information#

uint64_t Fg_getEventMask(
    Fg_Struct * fg,
    const char * name);

int Fg_getEventPayload(
    Fg_Struct * fg,
    uint64_t mask);

int Fg_getEventCount(
    Fg_Struct * fg);

const char * Fg_getEventName(
    Fg_Struct * fg,
    uint64_t mask);

If the name for an event source is known, the corresponding bit can be requested by calling the function Fg_getEventMask(). If the event name is not recognized, the function will return 0.

The size of the payload for any given event can be requested by calling the function Fg_getEventPayload(). The function will return a value greater or equal to zero if the event mask corresponds to a single valid event source, or a negative error code otherwise.

To iterate over all event sources, the function Fg_getEventCount() can be called to request the number of event sources supported by an applet, and the function Fg_getEventName() can be used to get the name of an event source. This is shown in the following example:

int numEvents = Fg_getEventCount(fg);
for (int event = 0; event < numEvents; ++event) {
    // get the event mask for each event
    const uint64_t eventMask = (1 << event);

    const char * eventName = Fg_getEventName(fg, eventMask);
    if (eventName != nullptr) {
        std::cout << "Event " << event << " is " << eventName << std::endl;
    }
}

Registering a Callback Function for Asynchronous Event Handling#

typedef int (* Fg_EventFunc_t)(
    uint64_t events,
    void * data,
    const struct fg_event_info * info);

int Fg_registerEventCallback(
    Fg_Struct * fg,
    uint64_t mask,
    Fg_EventFunc_t handler,
    void * data,
    unsigned int flags,
    struct fg_event_info * info);

int Fg_unregisterEventCallback(
    Fg_Struct * fg,
    uint64_t mask);

Info

In version 5.9 of the Runtime SDK the type struct fg_event_data was removed and the function Fg_unregisterEventCallback() was added. See section Unregistering a Callback Function for Asynchronous Event Handling in Plain C for the old interface.

Using Fg_registerEventCallback(), a function of type Fg_EventFunc_t can be registered to be called back when one or more events from a group of event sources is received. The function will be registered for a given frame grabber and an event mask and will be passed three parameters when called. The first parameter to the callback function is an event mask of all event sources for which events were received. The second parameter is a pointer which was supplied with the call to Fg_registerEventCallback() and which can be used as a pointer to a context structure or class, for example the this pointer of a class instance which implements image processing. The third parameter is a pointer to a struct fg_event_info which has to be allocated by the application and passed in the call to Fg_registerEventCallback() and which is used to store information about the events.

Only one callback function can be registered for the same group of events on any given frame grabber.

The callback function will be called from an event loop provided by the runtime. This implies that callback function is called in the thread context of the event loop. It also implies that the time the callback function takes until it returns is added to the general management overhead of the event loop, and that during this time multiple events might have been received. If any events were received during that time, the callback function will be called again immediately after it returns.

Through the parameter flags in the call to Fg_registerEventCallback(), the application can choose whether only a single event is delivered each time the callback function is called or if all pending events are grouped together. The default behavior when passing FG_EVENT_DEFAULT_FLAGS to the parameter flags is to deliver one event every time the callback function is called. By passing FG_EVENT_BATCHED all pending events will be grouped together.

After the callback function is no longer needed, it can be unregistered by calling Fg_unregisterEventCallback().

The following example shows how to register a callback function using a simple structure which holds information necessary for handling image acquisition:

struct EventUserCallbackData
{
    Fg_Struct * fg;
    fg_event_info info;
    uint64_t mask;
};

int EventUserCallback(uint64_t events, void * data,
                      const struct fg_event_info * info)
{
    auto context = reinterpret_cast<EventUserCallbackData *>(data);

    // process event(s)

    return 0;
}

void SetupEventUserCallback(EventUserCallbackData * context)
{
    // register callback function
    int result = Fg_registerEventCallback(
        context->fg, context->mask, &EventUserCallback, context,
        FG_EVENT_DEFAULT_FLAGS, &context->info);
    if (result != FG_OK) {
        throw std::runtime_error("Failed to register callback function");
    }
}

The allocation and management of the context structure is not shown in the example. The pointer has to be valid while the callback function remains registered. One solution can be to keep everything together in a C++ class. To use a callback function in a C++ class context, a static function can be used to register the callback handler, and the this pointer should be used as context data pointer which can be casted back to a class pointer and used accordingly.

Waiting for Events Synchronously#

uint64_t Fg_eventWait(
    Fg_Struct * fg,
    uint64_t mask,
    unsigned int timeout,
    unsigned int flags,
    struct fg_event_info * info);

The application can wait for an event from a group of event sources synchronously by calling Fg_eventWait(). The parameter flags allows choosing whether only a single event is returned each time the function is called or if all pending events are grouped together. The default behavior when passing FG_EVENT_DEFAULT_FLAGS to the parameter flags is to return at most one event every time the function is called. By passing FG_EVENT_BATCHED all pending events will be grouped together. The function will wait until either an event is received for any of the event sources specified in the parameter mask, or the number of seconds specified in the parameter timeout has elapsed. If no events were received until a timeout occurs the function will return 0, otherwise it will return a mask containing the event or the group of events received.

Activating Events#

int Fg_activateEvents(
    Fg_Struct * fg,
    uint64_t mask,
    int enable);

Before an event source can send events it has to be enabled. By calling Fg_activateEvents() a group of event sources can be enabled or disabled. If 1 is passed to the parameter enable, the group of event sources passed in the parameter mask is enabled. If 0 is passed to the parameter enable, they are disabled. All event sources should be disabled once they are no longer used by the application.

Since event sources can fire events any time after they were enabled, it is recommended to activate event sources only after either a callback has been registered for them using Fg_registerEventCallback(), or a separate thread is ready and waiting for events using Fg_eventWait().

Support for using a Frame Grabber from Multiple Processes#

Some applications might require access to a single frame grabber from different processes. One simple example is a process which monitors the activity of another process, for example an acquisition process. Another example might be separate acquisition processes using one camera each of a frame grabber with up to four cameras connected.

The Runtime SDK provides limited support for such scenarios with the master/slave mode. When using master/slave processes it is important to understand the limitations. It is also important to understand the general challenges that application developers meet when two or more processes have to be synchronized. While the Runtime SDK provides support for using a frame grabber from multiple processes, it does not support inter-process synchronization beyond very basic features to synchronize the startup of processes when initializing a frame grabber. For general purpose inter-process communication and synchronization, other operating system features or support libraries have to be used.

One particular limitation of the master/slave mode is that whenever two processes try to control the same feature on a frame grabber, the last process "wins" and overrides the actions of the first process. The first process might not even notice the override and make wrong assumptions as to the state of the frame grabber. (An experimental feature exists to synchronize the state between processes using inter-process communication, however, this approach has its own implications and will be discussed in section Experimental Support for Parameter and Acquisition Synchronization.)

The general recommendation is to design processes in such a way that they take on unique tasks (the tasks are disjunct with respect to applet parameters and features) and don't have to know about the existence of the other processes. One exception is a monitoring process which only accesses read-only features of an applet or gathers information from other processes through inter-process communication means.

Another limitation is that while only the master process will fully initialize the frame grabber, basic initialization has to be done in each process. This basic initialization can interfere with earlier processes if they already started image acquisition or camera discovery.

Synchronizing Processes During Initialization#

int Fg_InitLibrariesEx(
    const char * path,
    unsigned int flags,
    const char * id,
    unsigned int timeout);

void Fg_AbortInitLibraries();

void Fg_InitLibrariesStartNextSlave();
Fg_Struct * Fg_InitEx(
    const char * applet,
    unsigned int board,
    int flags);

Fg_Struct * Fg_InitConfigEx(
    const char * config,
    unsigned int board,
    int flags);

When synchronizing multiple processes, the synchronization point provided by the Runtime SDK is the call to Fg_InitLibrariesEx(). As with Fg_InitLibraries(), the first parameter to the call is not used and the application should always pass nullptr.

Processes to be synchronized form a group and synchronization will only consider processes within a group. The group of processes which should be synchronized is identified through the parameter id. This way, a complex application can have multiple independent synchronization groups. The string which identifies the group should not be empty and should consist only of characters that are allowed in file names on the operating system used. Starting an identifier used by an application with siso- is not recommended, the prefix siso- should be considered reserved for use by the runtime.

The synchronization behavior can be configured through the parameter flags. The following values and macros can be used in the parameter flags:

Flag Description
FG_INIT_LIBRARIES_SINGLE Default initialization, no synchronization
FG_INIT_LIBRARIES_MASTER Master initialization, prepare slave synchronization
FG_INIT_LIBRARIES_SLAVE Slave initialization, wait for master
FG_INIT_LIBRARIES_SEQUENTIAL Strictly sequential startup
FG_INIT_LIBRARIES_AUTOSTART_ON_INIT Start next slave on frame grabber initialization
FG_INIT_LIBRARIES_SET_SLAVE_PRIORITY(n) Set slave priority
FG_INIT_LIBRARIES_SET_NUMBER_OF_SLAVES(n) Set number of slaves

Using FG_INIT_LIBRARIES_SINGLE is the same as calling Fg_InitLibraries().

Using FG_INIT_LIBRARIES_SEQUENTIAL will only start the slave process with the highest priority from the master process. The slave process with the second highest priority has to be started from the slave process with the highest priority, and so on. This ensures a strict order of process initialization. If FG_INIT_LIBRARIES_SEQUENTIAL is used in a synchronization group, it has to be specified in all processes, including the master process. The macro FG_INIT_LIBRARIES_SET_SLAVE_PRIORITY() must be used in all slave processes when FG_INIT_LIBRARIES_SEQUENTIAL is used and defines the priority of the slave process. The highest priority is 1, the lowest priority is 63. Each process in a group must have a unique priority and priorities have to be assigned in a way not to leave gaps. (If process 2 wants to start process 3, but there is no process 3, then nobody will start process 4.) The macro FG_INIT_LIBRARIES_SET_NUMBER_OF_SLAVES() must be used in the master process when FG_INIT_LIBRARIES_SEQUENTIAL is used and defines the number of slave processes. The minimum value allowed is 1, the maximum is 63.

The last parameter in the call to Fg_InitLibrariesEx() specifies the time in milliseconds to wait for the process to be scheduled before an error is reported to the application.

The first process of the group, usually referred to as master process, establishes a group when Fg_InitLibrariesEx() is called. All following processes, usually referred to as slave processes, can choose to wait in the respective call to Fg_InitLibrariesEx() to be scheduled. Starting one or more slaves is triggered then by a call to Fg_InitLibrariesStartNextSlave() in the master process. The function Fg_InitLibrariesStartNextSlave() can be called automatically when a process initializes the frame grabber through a call to Fg_Init(), Fg_InitEx(), Fg_InitConfig() or Fg_InitConfigEx().

While the function Fg_InitLibraries() should never fail in normal circumstances, the same is not true for Fg_InitLibrariesEx() when using synchronization. Specify waiting times sensibly and handle errors accordingly.

In a multi-threaded application, waiting in a call to Fg_InitLibrariesEx() can be aborted from a different thread by calling Fg_AbortInitLibraries(). This will result in an error reported by the aborted call.

While in a master process, a call to Fg_Init() or Fg_InitConfig() can be used to initialize the frame grabber, all slave processes must call either Fg_InitEx() or Fg_InitConfigEx() and pass FG_INIT_FLAG_SLAVE in the parameter flags. Calling Fg_InitEx() or Fg_InitConfigEx() with FG_INIT_FLAG_DEFAULT is the same as calling Fg_Init() or Fg_InitConfig(), respectively.

The following example shows how a master process can set up a synchronization group for strictly sequential scheduling using two slave processes. The master uses a call to Fg_InitLibrariesStartNextSlave() to start the first slave process after some further initialization:

const char * syncGroupId = "example";

int result =
    Fg_InitLibrariesEx(
        nullptr,
        FG_INIT_LIBRARIES_MASTER
            | FG_INIT_LIBRARIES_SEQUENTIAL
            | FG_INIT_LIBRARIES_SET_NUMBER_OF_SLAVES(2),
        syncGroupId,
        0);

if (result == FG_OK) {
    const char * applet = "Acq_SingleCXP12Area";

    Fg_Struct * fg = Fg_Init(applet, 0);
    if (fg != nullptr) {
        // further initialization ...

        Fg_InitLibrariesStartNextSlave();

        // use frame grabber ...
    }

    Fg_FreeLibraries();
}

The slave processes in the example are identical save for the priority specified in the call to Fg_InitLibrariesEx(), only the first slave process is shown here. The slave process uses FG_INIT_LIBRARIES_AUTOSTART_ON_INIT to start the next slave process implicitly when Fg_InitEx() is called:

const char * syncGroupId = "example";

int result =
    Fg_InitLibrariesEx(
        nullptr,
        FG_INIT_LIBRARIES_SLAVE
            | FG_INIT_LIBRARIES_SEQUENTIAL
            | FG_INIT_LIBRARIES_AUTOSTART_ON_INIT
            | FG_INIT_LIBRARIES_SET_SLAVE_PRIORITY(1),
        syncGroupId,
        10000);

if (result == FG_OK) {
    const char * applet = "Acq_SingleCXP12Area";

    Fg_Struct * fg = Fg_InitEx(applet, 0, FG_INIT_FLAG_SLAVE);
    if (fg != nullptr) {
        // use frame grabber ...
    }

    Fg_FreeLibraries();
}

If the slave process is started before the master process, it will wait in the call to Fg_InitLibrariesEx() for the master process to be started. Once the master process calls Fg_InitLibrariesStartNextSlave() the slave process is scheduled to run and the call to Fg_InitLibrariesEx() returns FG_OK. If the master process is not started, or doesn't finish initialization before the waiting time of the slave runs out, then the slave cannot be scheduled to run, and the call to Fg_InitLibrariesEx() will return a timeout error.

The tool microDisplayX which is installed with the runtime supports master/slave synchronization and slave processes can synchronize to microDisplayX by using the group id siso-microdisplay-master. The flag FG_INIT_LIBRARIES_SEQUENTIAL is not supported by microDisplayX.

Experimental Support for Parameter and Acquisition Synchronization#

Info

The experimental support for parameter and acquisition synchronization was added in version 5.9 of the Runtime SDK.

The Runtime SDK provides experimental support to synchronize all parameter changes and the acquisition state across the process synchronization group. To enable parameter and acquisition synchronization, FG_INIT_FLAG_SLAVE_PARAM_SYNC has to be added to the parameter flags in the call to Fg_InitEx() or Fg_InitConfigEx() for all processes in the group.

There are a few things to consider when using parameter and acquisition synchronization, however.

First, the synchronization provided by the Runtime SDK uses inter-process communication mechanisms provided by the operating system. Because getting and setting parameters is no longer done locally in the process but has to be synchronized among several processes, both the latency and jitter for these operations increases. For most applications, these drawbacks should not matter. However, if the application uses a real-time operating system and getting or setting one or more parameters is considered real-time critical, parameter and acquisition synchronization should not be enabled. If FG_INIT_FLAG_SLAVE_PARAM_SYNC is not used explicitly, parameter and acquisition synchronization is not enabled and the experimental feature has no impact on the performance compared to earlier versions of the Runtime SDK.

Second, either the master or the slave processes can control acquisition when synchronization is used, never both. Acquisition in the master process can't be mixed with acquisition in one or more slave processes within a synchronization group. In most cases, the acquisition should be done in one or more slave processes, and FG_INIT_FLAG_ACQUISITION_SLAVE should be added to the parameter flags in the call to Fg_InitEx() or Fg_InitConfigEx() for the master process.

Support for Non-Uniform Memory Access#

int Fg_NumaPinThread(
    Fg_Struct * fg);

void * Fg_NumaAllocDmaBuffer(
    Fg_Struct * fg,
    size_t size);

int Fg_NumaFreeDmaBuffer(
    Fg_Struct * fg,
    void * ptr);

In multi-processor computers using non-uniform memory access (NUMA), each physical processor has its own memory and peripheral busses. As long as a process accesses only resources on the same physical processor it is running on, the access is almost as fast as on a single-processor computer and considerably faster than on a multi-processor computer using symmetric multiprocessing (SMP). However, if a process running on one physical processor wants to access resources which belong to another physical processor, the data has to be moved between two physical processors, slowing down the access. See the article about non-uniform memory access on Wikipedia for more information.

To support applications running on NUMA computers, all threads accessing a frame grabber should be pinned to the physical processor to which the device is attached locally. This can be done by calling Fg_NumaPinThread() in the context of each thread that accesses parameters or controls the acquisition on a frame grabber.

Furthermore, memory should be allocated locally on the same physical processor as well. If the application uses the allocation functions provided by the Runtime SDK this is done automatically. However, if the application uses Fg_AllocMemHead() and Fg_AddMem(), memory should be allocated using NUMA-aware memory allocation. The Runtime SDK provides the functions Fg_NumaAllocDmaBuffer() and Fg_NumaFreeDmaBuffer() to allocate and release memory on the same physical processor a frame grabber is attached to locally.

If the application uses other NUMA aware functions for allocating memory, the function Fg_getIntSystemInformationForBoardIndex() can be called using INFO_DRIVERGROUPAFFINITY to provide the driver IRQ group affinity, and the function Fg_getInt64SystemInformationForBoardIndex() can be called using INFO_DRIVERAFFINITYMASK to provide the driver IRQ processor affinity mask for the device.

There might be other limitations on some NUMA computers. Some NUMA computers for example are designed in a way to balance access to the peripheral busses and do not allow for a single device to use all or even most of the bandwidth. While this is beneficial to most server applications, for image acquisition and processing applications this strategy might limit the bandwidth available to a frame grabber device.

Considerations When Using Plain C#

When an application is limited to using a plain C compiler, some of the C++ wrappers provided for convenience or type-safety by the Runtime SDK cannot be used. This chapter will explain for each topic how to use the underlying functionality used by the C++ wrapper.

System Information in Plain C#

int Fg_getSystemInformation(
    Fg_Struct * fg,
    enum Fg_Info_Selector info,
    enum FgProperty property,
    int arg,
    void * buffer,
    unsigned int * size);

In sections General System Information and Board-Specific System Information of chapter System Information a set of functions is documented to request various information about the system in general or a specific frame grabber. The functions in these two chapters are all C++ wrappers for the function Fg_getSystemInformation().

The function Fg_getSystemInformation() will always return the requested information as a string which is stored in a buffer passed to the function. To allocate a buffer of sufficient size, the size of the buffer can be requested by initializing the variable used for the parameter size to 0 and passing NULL to the parameter buffer in the call. If the information requested is of a numeric type, the string has to be converted after successfully requesting the information.

To request information about the system in general, as documented in section General System Information, the function Fg_getSystemInformation() should be called with NULL passed to the parameter fg and 0 to the parameter arg.

For example, the number of frame grabbers installed in the computer can be requested as shown below:

int numBoards = 0;
char buffer[256];
unsigned int size = sizeof(buffer);
int result =
    Fg_getSystemInformation(NULL, INFO_NR_OF_BOARDS, PROP_ID_VALUE,
                            0, buffer, &size);
if (result == FG_OK) {
    numBoards = atoi(buffer);
    std::cout << "Number of boards: " << numBoards << std::endl;
}

To request information about a specific frame grabber, as documented in section Board-Specific System Information, the function Fg_getSystemInformation() expects either a handle to the frame grabber passed in the parameter fg, or a board index passed in the parameter arg.

For example, the following code requests the board type and name.

const int boardIndex = 0;

char buffer[256];
unsigned int size = sizeof(buffer);

int boardType = 0;
std::string boardName;
int result =
    Fg_getSystemInformation(NULL, INFO_BOARDTYPE, PROP_ID_VALUE,
                            boardIndex, buffer, &size);
if (result == FG_OK) {
    boardType = atoi(buffer);

    size = sizeof(buffer);
    result =
        Fg_getSystemInformation(NULL, INFO_BOARDNAME, PROP_ID_VALUE,
                                boardIndex, buffer, &size);
}
if (result == FG_OK) {
    boardName = buffer;

    std::cout << "Board #" << boardIndex
              << " is a " << boardName
              << " (type " << std::hex << boardType
              << std::dec << ")" << std::endl;
}

To request information which requires an additional argument on input, the buffer works two-ways. The additional argument has to be stored as a string in the buffer before the call. During the call the argument passed in the buffer is used and then overwritten, as the information requested is written to the buffer.

Accessing Parameter Values in Plain C#

int Fg_getParameterWithType(
    Fg_Struct * fg,
    int id,
    void * value,
    unsigned int dma,
    enum FgParamTypes type);

int Fg_freeParameterStringWithType(
    Fg_Struct * fg,
    int id,
    void * value,
    unsigned int dma,
    enum FgParamTypes type);
int Fg_setParameterWithType(
    Fg_Struct * fg,
    int id,
    const void * value,
    unsigned int dma,
    enum FgParamTypes type);

In section Accessing Parameter Values of chapter Working with Applet Parameters, C++ wrappers to request parameters with the most common types are documented. To access parameters from plain C, or to access parameters of a type for which no C++ wrapper exists, the functions Fg_getParameterWithType() and Fg_setParameterWithType() can be used. A variable of the matching type is needed for the call, and the corresponding FgParamTypes value has to be passed in the parameter type in the call. The type values are documented in section Parameter Types and the applet documentation should be consulted to know the type of a specific parameter.

For example, the following code sets the width of the first DMA channel of the applet to 1024.

const int dma = 0;
const int width = 1024;
int result = FG_INVALID_PARAMETER;
int paramId = Fg_getParameterIdByName(fg, "FG_WIDTH");
if (paramId > 0) {
    result =
        Fg_setParameterWithType(fg, paramId, &width, dma,
                                FG_PARAM_TYPE_INT32_T);
}

One special case when calling Fg_getParameterWithType() is the type FG_PARAM_TYPE_CHAR_PTR which represents a string parameter. While the length of the string needed to allocate a buffer of sufficient size can be requested by calling Fg_getParameterPropertyEx(), this is not the safest way to access dynamic parameters. A better approach is to request parameters with type FG_PARAM_TYPE_CHAR_PTR using FG_PARAM_TYPE_CHAR_PTR_PTR instead. This request will allocate a buffer of the right size, which can be released by calling Fg_freeParameterStringWithType() after the value is no longer needed.

The following example shows how to use FG_PARAM_TYPE_CHAR_PTR_PTR, assuming paramId is a parameter of type FG_PARAM_TYPE_CHAR_PTR:

const char * val = NULL;
int result =
    Fg_getParameterWithType(fg, paramId, &val, dma,
                            FG_PARAM_TYPE_CHAR_PTR_PTR);
if (result == FG_OK) {
    // use the string value stored in val ...

    Fg_freeParameterStringWithType(fg, paramId, val, dma,
                                   FG_PARAM_TYPE_CHAR_PTR_PTR);
}

The call to Fg_freeParameterStringWithType() receives a char *, not actually a char **.

Accessing Field Parameters#
struct FieldParameterAccess {
    enum FgParamTypes vtype;
    unsigned int index;
    unsigned int count;
    union {
        int32_t * p_int32_t;
        uint32_t * p_uint32_t;
        int64_t * p_int64_t;
        uint64_t * p_uint64_t;
        double * p_double;
    };
};

Field parameters are parameters which represent an array of two or more values of the same type. A typical example is a lookup table which can be used to remap pixel values. Field parameters can be of type FG_PARAM_TYPE_STRUCT_FIELDPARAMINT, FG_PARAM_TYPE_STRUCT_FIELDPARAMINT64 or FG_PARAM_TYPE_STRUCT_FIELDPARAMDOUBLE.

The size of a field parameter can be determined by requesting the parameter property PROP_ID_FIELD_SIZE as documented in section Accessing Parameter Properties of chapter Working with Applet Parameters. Also consult section Accessing Parameter Properties in Plain C if needed.

To request or change values in the array of a field parameter, the functions Fg_getParameterWithType() and Fg_setParameterWithType() should be called using an instance of struct FieldParameterAccess and FG_PARAM_TYPE_STRUCT_FIELDPARAMACCESS should be passed in the parameter type. The member vtype of struct FieldParameterAccess has to be set to the enum FgParamTypes value corresponding to the type of a single value in the field, not the field parameter type itself. The members index and count specify the offset into the array of the field parameter and the number of items requested or provided in the function call. Finally, the pointer in the union corresponding to the type of a single value in the field has to be initialized with a pre-allocated buffer which contains the values to be changed on input if Fg_setParameterWithType() is called, or the values currently stored in the array after Fg_getParameterWithType() was called.

The following example requests the first 256 values in a lookup table, assuming paramId is a parameter of type FG_PARAM_TYPE_STRUCT_FIELDPARAMINT and the field itself consists of at least 256 values of type FG_PARAM_TYPE_INT32_T:

int32_t field[256];
struct FieldParameterAccess fpa;
fpa.vtype = FG_PARAM_TYPE_INT32_T;
fpa.index = 0;
fpa.count = 256;
fpa.p_int32_t = &field;
int result =
    Fg_getParameterWithType(fg, paramId, &fpa, dma,
                            FG_PARAM_TYPE_STRUCT_FIELDPARAMACCESS);
if (result == FG_OK) {
    // work with the values in field ...
}

Accessing Parameter Properties in Plain C#

int Fg_getParameterProperty(
    Fg_Struct * fg,
    int id,
    enum FgProperty property,
    void * buffer,
    int * size);

int Fg_getParameterPropertyEx(
    Fg_Struct * fg,
    int id,
    enum FgProperty property,
    int dma,
    void * buffer,
    int * size);

In section Accessing Parameter Properties of chapter Working with Applet Parameters, C++ wrappers to request parameter properties with the most common types are documented. To access parameter properties from plain C, or to access parameter properties of a type for which no C++ wrapper exists, the function Fg_getParameterPropertyEx() can be used. Using the function Fg_getParameterProperty() is not recommended, as the function call implicitly uses DMA channel 0, parameter properties might differ though for different channels.

The function Fg_getParameterPropertyEx() will in all but one case return the requested information as a string which is stored in a buffer passed to the function. To allocate a buffer of sufficient size, the size of the buffer can be requested by initializing the variable used for the parameter size to 0 and passing NULL to the parameter buffer in the call. If the information requested is of a numeric type, the string has to be converted after successfully requesting the information.

The following example shows how to get the minimum value of a parameter, assuming paramId is a parameter of type FG_PARAM_TYPE_INT32_T:

char buffer[256];
int size = sizeof(buffer);

int32_t minVal = 0;
int result =
    Fg_getParameterPropertyEx(fg, paramId, PROP_ID_MIN, dma, buffer, &size);
if (result == FG_OK) {
    minVal = atoi(buffer);

    // work with the property ...
}
Accessing the Enum Values Parameter Property#
struct FgPropertyEnumValues {
    int32_t value;
    char name[1];
};

#define FG_PROP_GET_NEXT_ENUM_VALUE(pev) ...

When requesting the property PROP_ID_ENUM_VALUES of an enumeration parameter, the call to Fg_getParameterPropertyEx() will not return the property as a string. Instead, the buffer will be filled using struct FgPropertyEnumValues. The macro FG_PROP_GET_NEXT_ENUM_VALUE() can be used to iterate over the elements in the buffer. For convenience, the property PROP_ID_IS_ENUM will return the size of the buffer needed for an enumeration parameter.

The following example shows how to get and print the enum values property of a parameter, assuming paramId is an enumeration parameter:

const int defBufferSize = 256;

int size = defBufferSize;
char * buffer = malloc(size);

int result =
    Fg_getParameterPropertyEx(fg, paramId, PROP_ID_IS_ENUM,
                              dma, buffer, &size);
if (result == FG_OK) {
    int newSize = atoi(buffer);
    if (newSize > 0) {
        free(buffer);
        size = newSize;
        buffer = malloc(size);

        result =
            Fg_getParameterPropertyEx(fg, paramId, PROP_ID_ENUM_VALUES,
                                      dma, buffer, &size);
    } else {
        result = FG_INVALID_TYPE;
    }
}
if (result == FG_OK) {
    struct FgPropertyEnumValues * pev;
    for (pev = (struct FgPropertyEnumValues *)buffer;
         pev != NULL;
         pev = FG_PROP_GET_NEXT_ENUM_VALUE(pev)) {
            printf("%s is %d\n", pev->name, pev->value);
    }
}
free(buffer);

Registering a Callback Function for Asynchronous Mode in Plain C#

struct FgApcControl {
    unsigned int version;
    Fg_ApcFunc_t func;
    void *data;
    unsigned int timeout;
    unsigned int flags;
};

int Fg_registerApcHandler(
    Fg_Struct * fg,
    unsigned int dma,
    struct FgApcControl * control,
    enum FgApcControlFlags flags);

The function Fg_registerApcHandler() can be used to setup the asynchronous mode before starting the acquisition.

The callback function works in the same way as documented in section Registering a Callback Function for Asynchronous Mode of chapter Image Acquisition. When registering the callback function by calling the function Fg_registerCallbackHandler() the parameters which control the acquisition loop are passed in an instance of struct FgApcControl, whereas when calling the C++ wrapper function Fg_registerApcHandlerEx() these are passed as parameters to the function.

There are two places where flags are passed when registering the callback function. The parameter flags of the function Fg_registerApcHandler() does not control the acquisition loop and is not used. The application should always pass 0 to this parameter. Instead, the flags which control the acquisition loop are passed in the member flags of struct FgApcControl.

The following example shows how to register a callback function using a simple structure which holds information necessary for handling image acquisition:

struct ApcUserCallbackData
{
    Fg_Struct * fg;
    dma_mem * mem;
    unsigned int dma;
    unsigned int timeoutInSeconds;
    int mode;
};

int ApcUserCallback(frameindex_t frame, void * data)
{
    auto context = reinterpret_cast<ApcUserCallbackData *>(data);

    if (frame > 0) {
        // process new image ...
    } else {
        // handle error ...
    }

    return 0;
}

void SetupApcUserCallback(ApcUserCallbackData * context)
{
    // register callback function
    FgApcControl control;
    control.version = 0;
    control.func = &ApcUserCallback;
    control.data = context;
    control.timeout = context->timeoutInSeconds;
    control.flags = FG_APC_DELIVER_ERRORS | FG_APC_IGNORE_TIMEOUTS;

    int result = Fg_registerApcHandler(
        context->fg, context->dma, &control, 0);
    if (result != FG_OK) {
        throw std::runtime_error("Failed to register callback function");
    }
}

The allocation and management of the context structure is not shown in the example. The pointer has to be valid while the callback function remains registered. One solution can be to keep everything together in a C++ class. To use a callback function in a C++ class context, a static function can be used to register the callback handler, and the this pointer should be used as context data pointer which can be casted back to a class pointer and used accordingly.

After the callback function is no longer needed, it can be unregistered by calling Fg_registerApcHandler() by using the same frame grabber handle and DMA channel, but passing NULL to the parameter func:

int result = Fg_registerApcHandler(context->fg, context->dma, NULL, 0);

Unregistering a Callback Function for Asynchronous Event Handling in Plain C#

int Fg_registerEventCallback(
    Fg_Struct * fg,
    uint64_t mask,
    Fg_EventFunc_t handler,
    void * data,
    unsigned int flags,
    struct fg_event_info * info);

Using Fg_registerEventCallback(), a function of type Fg_EventFunc_t can be registered to be called back when one or more events from a group of event sources is received as described in section Registering a Callback Function for Asynchronous Event Handling of chapter Image Acquisition.

After the callback function is no longer needed, it can be unregistered by calling Fg_registerEventCallback(), passing the mask for the same group of events, FG_EVENT_DEFAULT_FLAGS to the parameter flags and NULL to the parameters handler, data and info:

// unregister event handler
Fg_registerEventCallback(fg, mask, NULL, NULL, FG_EVENT_DEFAULT_FLAGS, NULL);

  1. The group code is a bit field with each bit corresponding to a single feature of a license. Superset in this context means that for every bit in the applet group code the bit has to be set in the frame grabber group code. Subset in this context means that for every bit in the applet group code the bit has to be set in the frame grabber group code. If the applet uses features which a have not been activated through licensing on a frame grabber, the applet cannot be loaded by the runtime. 

  2. The value of a parameter can be set to any value in [PROP_ID_MIN; PROP_ID_MAX] in steps of PROP_ID_STEP according to the linear relation PROP_ID_VALUE = PROP_ID_MIN + n*PROP_ID_STEP, where n is in [0; (PROP_ID_MAX-PROP_ID_MIN)/PROP_ID_STEP[

  3. Older versions of the SDK used Fg_getLastPicNumberBlockingEx() in the acquisition loop, regardless of the acquisition model used. When ACQ_BLOCK is used, the new default behavior is to call Fg_getImageEx() with SEL_ACT_IMAGE when FG_APC_BATCH_IMAGES is set and SEL_NEXT_IMAGE otherwise. This means that in ACQ_BLOCK, the callback handler receives a buffer number, and not a frame number when the default behavior is used. Since there is no way to safely determine a buffer number for a given frame number when using ACQ_BLOCK, the use of FG_APC_OLD_ACQ_BLOCK_BEHAVIOR and calling Fg_getImageEx() from the callback handler is not recommended and should only be used when backwards compatibility of application code is absolutely necessary. 

Back to top