eGrabber Programmer's Guide 24.04.0.8

1 Introduction

The Application Programming Interface (API) for Coaxlink cards, Grablink Duo, and Gigelink is eGrabber, which is based on GenICam.

The goal of GenICam is to provide a standardized, uniform programming interface for using cameras and frame grabbers based on different physical interfaces (CoaXPress, GigE Vision, etc.) or from different vendors.

GenICam is a set of EMVA standards (GenApi and GenTL), as well as related conventions for naming things (the SFNC for standard features, the PFNC for pixel formats).

This document is meant to be read from beginning to end. Each chapter and section builds upon the preceding ones. If you skip parts of the text, some of the explanations and examples may seem cryptic. If that happens, you should go back and read the parts that you’ve skipped over.

2 GenApi

GenApi addresses the problem of configuring cameras. The way this is achieved is generic, and applies to different kinds of devices, including frame grabbers. In this chapter, everything we say about cameras also applies to frame grabbers.

GenApi requires two things to work: a register description, and a GenApi implementation.

2.1 Register description

A register description is an XML file that can be thought of as a computer-readable datasheet of the camera. It defines camera settings (such as PixelFormat and TriggerSource), and instructions on how to configure them (e.g., to set ExposureMode to Timed, write value 0x12 to register 0xE0140). It can also contain camera documentation.

2.2 GenApi implementation

A GenApi implementation is a software module that can read and interpret register description files.

The EMVA provides a reference implementation, but it is fairly difficult to use, and logging is very poor. Instead, we recommend using the Euresys implementation bundled with the eGrabber software package. This implementation also allows writing powerful configuration scripts.

2.3 Features

What the user gets from GenApi is a bunch of named features, organized in categories.

2.3.1 Set/get features

Set/get features are simple settings (called parameters in MultiCam), and can be of different types:

  • integer (e.g., Width)
  • float (e.g., AcquisitionFrameRate)
  • enumeration (e.g., PixelFormat)
  • boolean (e.g., LUTEnable)
  • string (e.g., DeviceVendorName)

The value of features can be retrieved/modified using get/set functions. Some features are read-only and some are write-only, but most allow read/write access.

2.3.2 Commands

There is also another kind of features: commands (e.g., AcquisitionStart). Commands are special: they don’t have any associated value; they have side effects. Command features are meant to be executed. When a command is executed, some action happens in the camera (e.g., a software trigger is generated). Obviously, get/set functions don’t make sense for commands and can’t be used.

3 GenTL

GenTL defines 5 types of objects, organized in a parent/child relationship:

  1. the system module
  2. the interface module
  3. the device module
  4. the data stream module
  5. the buffer module

Each module:

Additionally, all modules except the buffer module behave as ports that allow read/write operations. These port functions are used by GenApi to load that module’s description file, and to use its GenApi features.

3.1 System module

The system module (also referred to as TLSystem), represents the GenTL producer (e.g., the coaxlink.cti library). This module is at the top of the parent/child tree.

The system module provides basic information about the GenTL producer: things like the complete path to the CTI file and the vendor name (Euresys).

The real point of the system module is to list the interfaces (or frame grabbers) present in the system. The most important functions of the system module are TLGetNumInterfaces (to retrieve the number of frame grabbers in the system) and TLOpenInterface (to get access to one of the frame grabbers).

3.2 Interface module

The GenTL standard calls frame grabbers interfaces. The system module has one child interface for each frame grabber: if there are 2 Coaxlink cards in the computer, the system module will have two child interfaces.

Each interface represents a frame grabber. Global frame grabber features such as digital I/O lines belong in the interface module. This means that the GenApi features controlling the I/O lines are attached to the interface.

Each interface also acts as parent to one or several devices. The most important functions of the interface module are IFGetNumDevices (to retrieve the number of cameras that can be connected to the interface) and IFOpenDevice (to get access to one of the devices).

3.3 Device module

The GenTL standard uses the terms device and remote device for two related but different concepts. A remote device is a real camera, physically connected to a frame grabber. This is different from the device module we describe here.

The device module is the module that contains the frame grabber settings relating to the camera. This includes things like triggers and strobes.

The device module also acts as parent to one or several data streams, and can be viewed as the sibling of the remote device. The most important functions of the device module are DevOpenDataStream (to get access to one of the data streams) and DevGetPort (to get access to the remote device).

3.4 Data stream module

The data stream module handles buffers. During acquisition runs, images are sent from the camera to the frame grabber, which transfers them to memory buffers allocated on the host computer. The data stream module is where image acquisition occurs. It is where most of the functionality resides.

Buffer handling is very flexible. Any number of buffers can be used. Buffers are either in the input queue, in the output queue, or temporarily unqueued. The application decides when empty buffers are queued (to the input FIFO), and when filled buffers are popped (from the output FIFO).

3.5 Buffer module

The buffer module simply represents a memory buffer given to a parent data stream. Useful metadata is associated to buffers. This includes the image width, height, pixel format, timestamp… These are retrieved through info commands (see BUFFER_INFO_CMD_LIST in the standard GenTL header file).

The buffer module is the only module that doesn’t have read/write port functions; it doesn’t have GenApi features.

3.6 GenTL API

GenTL makes it possible to detect, control and use all camera and frame grabber features, but its usage is tedious:

Instead of using the GenTL API directly, we recommend using either:

4 EGenTL

Euresys::EGenTL is a C++ class that provides the same functionality as standard GenICam GenTL, but with a more user-friendly interface. For example, it uses std::string instead of raw char pointers, and error codes are transformed into exceptions. Euresys::EGenTL also takes care of locating the GenTL producer and loading the functions it exports.

To use Euresys::EGenTL, simply include the relevant header file1:

#include <EGenTL.h>

Instead of the raw, low-level C functions that GenTL defines, we get a Euresys::EGenTL object that represents the GenTL producer:

4.1 A first example

This program uses Euresys::EGenTL to iterate over the Coaxlink cards present in the system, and display their id:

  1. Include EGenTL.h, which contains the definition of the Euresys::EGenTL class.

  2. Create a Euresys::EGenTL object. This involves the following operations:

    • locate and dynamically load the Coaxlink GenTL producer (coaxlink.cti);
    • retrieve pointers to the functions exported by coaxlink.cti, and make them available via Euresys::EGenTL methods;
    • initialize coaxlink.cti (this is done by calling the GenTL initialization function GCInitLib).
  3. Open the GenTL producer. This returns a handle of type GenTL::TL_HANDLE. The GenTL namespace is defined in the standard GenTL header file, which has been automatically included by EGenTL.h in step 1.

  4. Find out how many cards are present in the system.

  5. Retrieve the id of the n-th card.

  6. Euresys::EGenTL uses exceptions to report errors, so we wrap our code inside a try ... catch block.

Example of program output:

[0] PC1633 - Coaxlink Quad G3 (1-camera, line-scan) - KQG00014
[1] PC1632 - Coaxlink Quad (1-camera) - KQU00031

4.2 Relevant files

include/EGenTL.h Main header. Includes all the other headers. Defines Euresys::EGenTL.
include/GenTL_v1_5.h Standard GenTL header. Defines standard types, functions and constants.
include/GenTL_v1_5_EuresysCustom.h Defines eGrabber-specific constants.

5 eGrabber

eGrabber is a library of C++ classes that provide a high-level interface. It is built on top of EGenTL, and is recommended for most users.

A .NET assembly, built on top of eGrabber, is also provided. In this document, we focus mainly on the C++ API. Minor differences between the C++ and .NET interfaces are listed in a dedicated chapter.

Python bindings are also available for eGrabber. Again, the differences between the C++ and Python interfaces are listed in a dedicated chapter.

To use the classes described here, you need to include the main eGrabber file:

#include <EGrabber.h>

eGrabber2 comprises several classes, the most important of which is Euresys::EGrabber:

namespace Euresys {
    class EGrabber;
}

In this text, we’ll refer to this class as a grabber. A grabber encapsulates a set of related GenTL modules:

Go back to the chapter about GenTL modules if these concepts are not clear.

5.1 A first example

This example creates a grabber and displays basic information about the interface, device, and remote device modules it contains:

  1. Include EGrabber.h, which defines the Euresys::EGrabber class, and includes the other header files we need (such as EGenTL.h and the standard GenTL header file).

  2. Create a Euresys::EGenTL object. This involves the following operations:

    • locate and dynamically load the Coaxlink GenTL producer (coaxlink.cti);
    • retrieve pointers to the functions exported by coaxlink.cti, and make them available via Euresys::EGenTL methods;
    • initialize coaxlink.cti (this is done by calling the GenTL initialization function GCInitLib).
  3. Create a Euresys::EGrabber object. The constructor needs the gentl object created in step 2. It also takes as optional arguments the indices of the interface and device to use.

    The purpose of the angle brackets (<>) that come after EGrabber will become clear later. For now, they can be safely ignored.

  4. Use GenApi to find out the ID of the Coaxlink card. Euresys::InterfaceModule indicates that we want an answer from the interface module.

  5. Similarly, find out the ID of the device. This time, we use Euresys::DeviceModule to target the device module.

  6. Finally, read the camera resolution. Euresys::RemoteModule indicates that the value must be retrieved from the camera.

  7. eGrabber uses exceptions to report errors, so we wrap our code inside a try ... catch block.

Example of program output:

Interface:    PC1633 - Coaxlink Quad G3 (2-camera) - KQG00014
Device:       Device0
Resolution:   4096x4096

5.2 Discovering grabbers and cameras

5.2.1 Using indices

In the previous example, we created a grabber object using the indices of the interface (CARD_IX) and device (DEVICE_IX) to use:

Euresys::EGrabber<> grabber(gentl, CARD_IX, DEVICE_IX);

Those indices are optional and are set to 0 by default. We have already seen earlier that GenTL modules are organized as a tree structure where the root is the system module (please refer to the chapter about GenTL modules for details). In short, the system module maintains a list of interfaces present in the system, each interface maintains a list of devices associated to that interface, and each device maintains a list of available data streams. So using 3 indices, we can identify a specific data stream within the GenTL module hierarchy. Each index represents the position of an element in 3 successive lists (interface, device and stream lists).

The GenTL standard defines two functions to update those lists:

  • TLUpdateInterfaceList: to update the list of available interfaces in the system;
  • IFUpdateDeviceList: to update the list of available devices connected to a specific interface.

Please note that the GenTL standard mandates that those lists cannot change between calls to their corresponding update functions. In other words, when TLUpdateInterfaceList is called, a “snapshot” of the present interfaces is taken and maintained in the GenTL library until the next call. The same applies to IFUpdateDeviceList for the available devices of a specific interface.

To simplify the creation of a grabber object, the EGrabber constructor calls the “update list” functions automatically so that the indices given as arguments refer to the current state of the system.

Those constructors are fine and easy to use when the system hierarchy is fixed, i.e. when it does not change dynamically.

Coaxlink and Grablink cards are fixed interfaces in the system and those interfaces have a fixed number of devices that depends on the selected firmware. Calling several times the “update list” functions from the same process on such fixed systems will lead to the same results; therefore an application can safely use the indices to identify specific modules and create grabbers.

However, this simple approach may lead to troubles on systems that change dynamically. The Gigelink producer is a system that uses network interfaces to find and establish connections to GigE Vision cameras. Obviously, calling IFUpdateDeviceList on such systems will lead to completely different results depending on what’s available on the network when the function is invoked. In such environments, referring to a specific data stream using indices might not be applicable.

To solve this issue, and to provide a simple way to discover possible grabber objects and connected cameras in the system, we provide the Euresys::EGrabberDiscovery module.

5.2.2 Using EGrabberDiscovery

The Euresys::EGrabberDiscovery module provides a function discover that scans the system to detect available grabbers and cameras. Here is an example:

  1. Create a Euresys::EGrabberDiscovery object for a specific producer (gentl argument). The object takes exclusive control of the producer interface and device lists. As long as the discovery object exists, it prevents any other module to update the lists.

  2. Update the producer interface and device lists and scan the system to discover the available GenTL modules as well as the connected cameras at that moment.

  3. Walk through the discovered grabbers.

  4. For each discovered grabber, create a Euresys::EGrabber object; the function discovery::egrabbers returns the relevant information that identifies the discovered grabber for which a Euresys::EGrabber instance should be created.

  5. Walk through the discovered cameras.

  6. For each discovered camera, create a Euresys::EGrabber object; the function discovery::cameras returns the relevant information that identifies the discovered camera for which a Euresys::EGrabber instance should be created.

Remarks

  • The Euresys::EGrabberDiscovery provides two ways of exploring the system, a grabber-oriented one and a camera-oriented one. The camera-oriented discovery is useful in at least two situations:
    • When an application needs to configure and/or use the cameras connected to the system (and simply ignore the unused grabbers).
    • When a multi-bank camera is connected to the system; a multi-bank camera is a camera composed by several cameras (which act as independent CoaXPress devices). In this case, each camera bank is connected to a specific GenTL device and exposes a data stream. The camera-oriented discovery is able3 to (1) detect automatically the different banks that make up the multi-bank camera, (2) reorder them, and (3) expose them as a unified camera that can be easily instantiated as an Euresys::EGrabber object. The resulting Euresys::EGrabber object hides the complexity of the banks and behaves as a usual EGrabber object as far as acquisition is concerned.
  • As long as Euresys::EGrabberDiscovery exists, any attempt to update interface or device lists will trigger a not_allowed exception; this will happen if a Euresys::EGrabber object is created using the index-based constructor instead of the “discovery” variants because the index-based constructor updates the interface and device lists before opening the requested GenTL modules.

Please refer to the Euresys::EGrabberDiscovery module documentation for a detailed description of the API.

5.3 Acquiring images

This program uses eGrabber to acquire images from a camera connected to a Coaxlink card:

  1. Create a Euresys::EGrabber object. The second and third arguments of the constructor are omitted here. The grabber will use the first device of the first interface present in the system.

  2. Allocate 3 buffers. The grabber automatically determines the required buffer size.

  3. Start the grabber. Here, we ask the grabber to fill 10 buffers. If we don’t want the grabber to stop after a specific number of buffers, we can do grabber.start(GenTL::GENTL_INFINITE), or simply grabber.start().

    Starting the grabber involves the following operations:

    • the AcquisitionStart command is executed on the camera;
    • the DSStartAcquisition function is called to start the data stream.

    In this example, we assume that the camera and frame grabber are properly configured. For a real application, it would be safer to run a configuration script before starting acquisitions (and before allocating buffers for that matter). This will be shown in another example.

  4. Wait for a buffer filled by the grabber. The result is a Euresys::ScopedBuffer. The term scoped is used to indicate that the lifetime of the buffer is the current scope (i.e., the current block).

  5. Retrieve the buffer address. This is done by calling the getInfo method of the buffer. This method takes as argument a BUFFER_INFO_CMD. In this case, we request the BUFFER_INFO_BASE, which is defined in the standard GenTL header file:

    Notice that getInfo is a template method, and when we call it we must specify the type of value we expect. BUFFER_INFO_BASE returns a pointer; this is why we use getInfo<void *>.

  6. Do the same to retrieve the timestamp of the buffer. This time, we use the uint64_t version of getInfo to match the type of BUFFER_INFO_TIMESTAMP.

    Note that timestamps are always 64-bit and expressed as the number of microseconds that have elapsed since the computer was started.

  7. We reach the end of the for block. The local variable buf gets out of scope and is destroyed: the ScopedBuffer destructor is called. This causes the GenTL buffer contained in buf to be re-queued (given back) to the data stream of the grabber.

Example of program output:

buffer address: 0x7f3c32c54010, timestamp: 11247531003686 us
buffer address: 0x7f3c2c4bf010, timestamp: 11247531058080 us
buffer address: 0x7f3c2c37e010, timestamp: 11247531085003 us
buffer address: 0x7f3c32c54010, timestamp: 11247531111944 us
buffer address: 0x7f3c2c4bf010, timestamp: 11247531137956 us
buffer address: 0x7f3c2c37e010, timestamp: 11247531163306 us
buffer address: 0x7f3c32c54010, timestamp: 11247531188600 us
buffer address: 0x7f3c2c4bf010, timestamp: 11247531213807 us
buffer address: 0x7f3c2c37e010, timestamp: 11247531239158 us
buffer address: 0x7f3c32c54010, timestamp: 11247531265053 us

We can see that the three buffers that were allocated (let’s call them A at 0x7f3c32c54010, B at 0x7f3c2c4bf010, and C at 0x7f3c2c37e010) are used in a round-robin fashion: A → B → C → A → B → C → … This is the result of:

5.4 Configuring the grabber

Configuration is a very important aspect of any image acquisition program.

Configuration basically boils down to a series of set/get and/or execute operations performed on the grabber modules: the remote device (i.e., the camera), the interface, the device, or the data stream modules.

This program configures the grabber for the so-called RG control mode (asynchronous reset camera control, frame grabber-controlled exposure).

  1. Enable triggers on the camera.

  2. Tell the camera to look for triggers on the CoaXPress link.

  3. Configure the camera to use the TriggerWidth exposure mode.

  4. Execute the DeviceReset command to restore default settings on the device module.

  5. Set the frame grabber’s camera control method to RG. In this mode, camera cycles are initiated by the frame grabber, and the exposure duration is also controlled by the frame grabber.

  6. Tell the frame grabber to initiate camera cycles itself (at a rate defined by CycleMinimumPeriod), without waiting for hardware or software triggers.

  7. Configure the frame rate.

But there is a better way to configure the grabber. Using a script file, the program becomes:

and the configuration script is:

Using a script file has several advantages:

5.5 Events

5.5.1 Background

Coaxlink cards generate different kinds of events:

  • New buffer events: events indicating that a buffer has been filled by a data stream.
  • Data stream events: events related to a data stream and its frame store.
  • Camera and illumination controller events: events related to the real-time control (performed by a device) of a camera and its illumination devices.
  • I/O toolbox events: events (coming from the interface) related to digital I/O lines and other I/O tools.
  • CoaXPress interface events: events (also coming from the interface) related to the CoaXPress interface.

New buffer events are standard in GenTL. They occur when a buffer is filled by the frame grabber. Information attached to new buffer events include the handle of the buffer and a timestamp.

The other types of events are restricted to Coaxlink and can be viewed as categories of specific events. For example, in the CIC category of events, we have:

  • CameraTriggerRisingEdge (start of camera trigger)
  • CameraTriggerFallingEdge (end of camera trigger)
  • StrobeRisingEdge (start of light strobe)
  • StrobeFallingEdge (end of light strobe)
  • AllowNextCycle (CIC is ready for next camera cycle)

and in the I/O toolbox category of events, we have:

  • LIN1 (line input tool 1)
  • LIN2 (line input tool 2)
  • MDV1 (multiplier/divider tool 1)

5.5.2 Counters

Coaxlink firmware counts each occurrence of each event (except new buffer events) and makes this counter available in a GenApi feature named EventCount. Each event has its own counter, and the value of EventCount depends on the selected event:

or, using the selected feature notation:

5.5.3 Notifications

As we’ve just seen, when an event occurs, a dedicated counter is incremented. Coaxlink can also notify the application of this event by having Euresys::EGrabber execute a user-defined callback function. But first, it is required to enable notifications of one or more events:

or:

Using a configuration script, it is easy to enable notifications for all events:

  1. Define a helper function named enableAllEvents and taking as argument a module (or port) p.

  2. Use the $ee function to retrieve the list of values EventSelector can take. This is the list of events generated by module p. (ee stands for enum entry.)

  3. For each event, enable notifications. (The + operator concatenates strings, so if e is 'LIN1', the expression 'EventNotification[' + e + ']' evaluates to 'EventNotification[LIN1]'.)

  4. Call the enableAllEvents function defined in step 1 for the interface module. This will enable notifications for all events in the I/O toolbox and CoaXPress interface categories.

  5. Likewise, enable notifications for all events coming from the device module (CIC events).

  6. Finally, enable notifications for all data stream events.

5.5.4 Callback functions

When an event occurs, and event notification is enabled for that event, Euresys::EGrabber executes one of several callback functions.

These callback functions are defined in overridden virtual methods:

As you can see, a different callback function can be defined for each category of events.

In .NET, callback functions are defined by creating delegates rather than overriding virtual methods. An example will be given in the chapter about the .NET assembly.

5.5.5 Event identification

When an event is notified to the application, the callback function that is executed indicates the category of that event. The actual event that occurred is identified by a numerical ID, called numid, and defined in include/GenTL_v1_5_EuresysCustom.h:

For reference, the following table lists the relationships between:

  • the module generating events
  • the category of events
  • the name of the callback function
  • the data type passed to the callback function
  • the common numid prefix
Module Category Callback function Data type numid prefix
Data stream New Buffer onNewBufferEvent NewBufferData -
Data stream Data Stream onDataStreamEvent DataStreamData EVENT_DATA_NUMID_DATASTREAM_
Device CIC onCicEvent CicData EVENT_DATA_NUMID_CIC_
Interface I/O Toolbox onIoToolboxEvent IoToolboxData EVENT_DATA_NUMID_IO_TOOLBOX_
Interface CXP Interface onCxpInterfaceEvent CxpInterfaceData EVENT_DATA_NUMID_CXP_INTERFACE_

Note that:

  • There is only one event in the new buffer event category, so we don’t need a numid there.

  • A simple naming scheme is followed: a category of events named some category has a callback function named onSomeCategoryEvent which takes as argument a SomeCategoryData structure, and uses EVENT_DATA_NUMID_SOME_CATEGORY_ as common numid prefix.

5.5.6 Examples

We’ll soon show a few complete example programs illustrating events and callbacks, but there is one more thing we need to explain before we can do that: the context in which callback functions are executed. This is the subject of the next section.

5.6 EGrabber flavors

When should the callback functions be called? From which context (i.e., which thread)? Thinking about these questions leads to the definition of several callback models:

These three callback models all make sense, and each one is best suited for some applications.

To give the user maximum flexibility, we support all three callback models. This is why Euresys::EGrabber exists in different flavors. So far, we have eluded the meaning of the angle brackets in EGrabber<>. The EGrabber class is actually a template class, i.e., a class that is parameterized by another type:

5.7 Events and callbacks examples

5.7.1 On demand callbacks

This program displays basic information about CIC events generated by a grabber:

  1. This using directive allows writing Xyz instead of Euresys::Xyz. This helps keep lines relatively short.

  2. Define a new class MyGrabber which is derived from EGrabber<CallbackOnDemand>.

  3. MyGrabber’s constructor initializes its base class by calling EGrabber<CallbackOnDemand>’s constructor.

  4. Run a config.js script which should:

    • properly configure the camera and frame grabber;
    • enable notifications for CIC events.
  5. Enable onCicEvent callbacks.

  6. The onCicEvent callback function receives a const CicData &. This structure is defined in include/EGrabberTypes.h. It contains a few pieces of information about the event that occurred. Here, we display the timestamp and numid of each event. The numid indicates which CIC event occurred.

  7. Call processEvent<CicData>(1000):

    • the grabber starts waiting for a CIC event;
    • if an event occurs within 1000 ms, the grabber executes the onCicEvent callback function;
    • otherwise, a timeout exception will be thrown.

Example of program output:

timestamp: 1502091779 us, numid: 0x8041 (Start of camera trigger)
timestamp: 1502091784 us, numid: 0x8048 (Received acknowledgement for previous CXP trigger message)
timestamp: 1502091879 us, numid: 0x8043 (Start of light strobe)
timestamp: 1502092879 us, numid: 0x8044 (End of light strobe)
timestamp: 1502097279 us, numid: 0x8042 (End of camera trigger)
timestamp: 1502097284 us, numid: 0x8048 (Received acknowledgement for previous CXP trigger message)
timestamp: 1502191783 us, numid: 0x8041 (Start of camera trigger)
timestamp: 1502191783 us, numid: 0x8045 (CIC is ready for next camera cycle)
timestamp: 1502191788 us, numid: 0x8048 (Received acknowledgement for previous CXP trigger message)
timestamp: 1502191883 us, numid: 0x8043 (Start of light strobe)
timestamp: 1502192883 us, numid: 0x8044 (End of light strobe)
timestamp: 1502197283 us, numid: 0x8042 (End of camera trigger)
timestamp: 1502197288 us, numid: 0x8048 (Received acknowledgement for previous CXP trigger message)
timestamp: 1502291788 us, numid: 0x8041 (Start of camera trigger)
timestamp: 1502291788 us, numid: 0x8045 (CIC is ready for next camera cycle)
...

5.7.2 Single thread and multi thread callbacks

This program displays basic information about CIC events generated by a grabber, this time using the CallbackSingleThread model.

There are very few differences between this program and the CallbackOnDemand version:

  1. MyGrabber is derived from EGrabber<CallbackSingleThread> instead of EGrabber<CallbackOnDemand>.

  2. Consequently, MyGrabber’s constructor initializes its base class by calling EGrabber<CallbackSingleThread>’s constructor.

  3. EGrabber creates a callback thread in which it calls processEvent, so we don’t have to. Here, we simply enter an infinite loop.

As you can see, moving from CallbackOnDemand to CallbackSingleThread is very simple. If instead you want the CallbackMultiThread variant, simply change the base class of MyGrabber to EGrabber<CallbackMultiThread> (and call the appropriate constructor).

5.7.3 New buffer callbacks

This program shows how to access information related to new buffer events. It uses CallbackMultiThread, but it could use another callback method just as well.

  1. In onNewBufferEvent, create a temporary ScopedBuffer object buf. The ScopedBuffer constructor takes two arguments:

    • the grabber owning the buffer: since we are in a class derived from EGrabber, we simply pass *this;
    • information about the buffer: this is provided in data.
  2. Retrieve the timestamp of the buffer, which is defined as the time at which the camera started to send data to the frame grabber. Note that Euresys::gc is an alias for the standard GenTL C++ namespace.

  3. As explained in the section about event identification, new buffer events are slightly different from the other kinds of events: they are standard (as per GenTL), and don’t have an associated numid.

    As a consequence, the NewBufferData structure passed to onNewBufferEvent doesn’t have a numid field. It does, however, have a timestamp field indicating the time at which the driver was notified that data transfer to the buffer was complete. This event timestamp is inevitably greater than the buffer timestamp retrieved in step 2.

  4. We reach the end of the block where the local variable buf has been created. It gets out of scope and is destroyed: the ScopedBuffer destructor is called. This causes the GenTL buffer contained in buf to be re-queued (given back) to the data stream of the grabber.

Example of program output:

event timestamp: 77185931621 us, buffer timestamp: 77185919807 us
event timestamp: 77185951618 us, buffer timestamp: 77185939809 us
event timestamp: 77185971625 us, buffer timestamp: 77185959810 us
event timestamp: 77185991611 us, buffer timestamp: 77185979812 us
event timestamp: 77186011605 us, buffer timestamp: 77185999808 us
event timestamp: 77186031622 us, buffer timestamp: 77186019809 us
event timestamp: 77186051614 us, buffer timestamp: 77186039810 us
event timestamp: 77186071611 us, buffer timestamp: 77186059811 us
event timestamp: 77186091602 us, buffer timestamp: 77186079812 us
event timestamp: 77186111607 us, buffer timestamp: 77186099814 us

5.8 Relevant files

include/EGrabber.h Main header. Includes all the other headers except include/FormatConverter.h. Defines Euresys::EGrabber, Euresys::Buffer, Euresys::ScopedBuffer.
include/EGrabberDiscovery.h Defines Euresys::EGrabberDiscovery.
include/EGrabberTypes.h Defines data types related to Euresys::EGrabber.
include/EGenTL.h Defines Euresys::EGenTL.
include/GenTL_v1_5.h Standard GenTL header. Defines standard types, functions and constants.
include/GenTL_v1_5_EuresysCustom.h Defines eGrabber-specific constants.
include/FormatConverter.h Defines Euresys::FormatConverter helper class.

6 Euresys GenApi scripts

The Euresys GenApi Script language is documented in a few GenApi scripts. For convenience, they are also included here.

6.1 doc/basics.js

// Euresys GenApi Script uses a syntax inspired by JavaScript, but is not
// exactly JavaScript. Using the extension .js for scripts is just a way to get
// proper syntax highlighting in text editors.
//
// This file describes the basics of Euresys GenApi Script. It can be executed
// by running 'gentl script <path-to-coaxlink-scripts-dir>/doc/basics.js', or
// more simply 'gentl script egrabber://doc/basics.js'.


// Euresys GenApi Script is case-sensitive.

// Statements are always separated by semicolons (JavaScript is more
// permissive).

// Single-line comment

/* Multi-line comment
   (cannot be nested)
*/

// Function declaration
function multiply(a, b) {
    return a * b;
}

// Functions can be nested
function sumOfSquares(a, b) {
    function square(x) {
        return x * x;
    }
    return square(a) + square(b);
}

// Variable declaration
function Variables() {
    var x = 1;      // 1
    var y = 2 * x;  // 2
    var z;          // undefined
}

// Data types
function DataTypes() {
    // Primitive types: Boolean, Number, String, undefined
    function Booleans() {
        var x = true;
        var y = false;
    }
    function Numbers() {
        var x = 3.14159;
        var y = -1;
        var z = 6.022e23;
    }
    function Strings() {
        var empty = "";
        var x = "euresys";
        var y = 'coaxlink';
        var z = x + ' ' + y; // euresys coaxlink
        assertEqual(x, z.slice(0, 7));
        assertEqual(y, z.slice(8));
    }
    function Undefined() {
        // undefined is the type of variables without a value
        // undefined is also a special value
        var x; // undefined
        x = 1; // x has a value
        x = undefined; // x is now again undefined
    }
    // Objects: Object (unordered set of key/value pairs), Array (ordered list
    // of values), RegExp (regular expression)
    function Objects() {
        // Construction
        var empty = {};
        var x = { a: 1, b: 2, c: 3 };
        var y = { other: x };
        // Access to object properties
        var sum1 = x.a + x.b + x.c; // dot notation
        var sum2 = x['a'] + x['b'] + x['c']; // bracket notation
        // Adding properties
        x.d = 4;    // x: { a: 1, b: 2, c: 3, d: 4 }
        x["e"] = 5; // x: { a: 1, b: 2, c: 3, d: 4, e: 5 }
    }
    function Arrays() {
        // Construction
        var empty = [];
        var x = [3.14159, 2.71828];
        var mix = [1, false, "abc", {}];
        // Access to array elements
        var sum = x[0] + x[1]; // bracket notation
        // Adding elements
        x[2] = 1.61803; // x: [3.14159, 2.71828, 1.61803]
        x[4] = 1.41421; // x: [3.14159, 2.71828, 1.61803, undefined, 1.41421];
    }
    function RegularExpressions() {
        var x = /CXP[36]_X[124]/;
    }
    Booleans();
    Numbers();
    Strings();
    Undefined();
    Objects();
    Arrays();
}

// Like JavaScript, Euresys GenApi Script is a dynamically typed language. The
// type of a variable is defined by the value it holds, which can change.
function DynamicVariables() {
    var x = 1; // Number
    x = "x is now a string";
}

// Object types are accessed by reference.
function References() {
    var x = [3.14159, 2.71828]; // x is a reference to an array
    var y = x;                  // y is a reference to the same array
    assertEqual(x.length, y.length);
    assertEqual(x[0], y[0]);
    assertEqual(x[1], y[1]);
    y[2] = 1.61803;             // the array can be modified via any reference
    assertEqual(x[2], y[2]);

    function update(obj) {
        // objects (including arrays) are passed by reference
        obj.updated = true;
        obj.added = true;
    }
    var z = { initialized: true, updated: false };
    assertEqual(true, z.initialized);
    assertEqual(false, z.updated);
    assertEqual(undefined, z.added);
    update(z);
    assertEqual(true, z.initialized);
    assertEqual(true, z.updated);
    assertEqual(true, z.added);
}

// Supported operators
function Operators() {
    // From lowest to highest precedence:
    // Assignment operators: = += -= *= /=
    var x = 3;
    x += 2;
    x -= 1;
    x *= 3;
    x /= 5;
    assertEqual(2.4, x);
    // Logical OR: ||
    assertEqual(true, false || true);
    assertEqual('ok', false || 'ok');
    assertEqual('ok', 'ok' || 'ignored');
    // Logical AND: &&
    assertEqual(false, true && false);
    assertEqual(true, true && true);
    assertEqual('ok', true && 'ok');
    // Identity (strict equality) and non-identity (strict inequality): === !==
    assertEqual(true, 1 === 2 / 2);
    assertEqual(true, 1 !== 2);
    // Equality (==) and inequality (!=) JavaScript operators lead to confusing
    // and inconsistent conversions of their operands. They are not implemented
    // in Euresys GenApi Script.
    // Relational operators: < <= > >=
    assert(1 < 2);
    assert(1 <= 1);
    assert(2 > 1);
    assert(2 >= 2);
    // Addition and subtraction: + -
    assertEqual(1, 3 - 2);
    assertEqual(5, 2 + 3);
    assertEqual("abcdef", "abc" + "def"); // if one of the operands is of type
    assertEqual("abc123", "abc" + 123);   // string, all operands are converted
    assertEqual("123456", 123 + "456");   // to string, and concatenated
    // Multiplication and division: * /
    assertEqual(4.5, 3 * 3 / 2);
    // Prefix operators: ++ -- ! typeof
    var x = 0;
    assertEqual(1, ++x);
    assertEqual(1, x);
    assertEqual(0, --x);
    assertEqual(0, x);
    assertEqual(true, !false);
    assertEqual('boolean', typeof false);
    assertEqual('number', typeof 0);
    assertEqual('string', typeof '');
    assertEqual('undefined', typeof undefined);
    assertEqual('function', typeof function () {});
    assertEqual('object', typeof {});
    assertEqual('object', typeof []);
    assertEqual('object', typeof /re/);
    assertEqual('object', typeof null);
    // Postfix operators: ++ --
    var x = 0;
    assertEqual(0, x++);
    assertEqual(1, x);
    assertEqual(1, x--);
    assertEqual(0, x);
    // Function call: ()
    assertEqual(6, multiply(3, 2));
    assertEqual(13, sumOfSquares(3, 2));
    // Member access: . []
    var obj = { a: 1 };
    assertEqual(1, obj.a);
    obj['4'] = 'four';
    assertEqual('four', obj[2*2]);
}

// Scope of variables
function OuterFunction() {
    var x = 'outer x';
    function Shadowing() {
        assertEqual(undefined, x);
        var x = 'inner x';
        assertEqual('inner x', x);
    }
    function Nested() {
        assertEqual('outer x', x);
        var y = 'not accessible outside Nested';
        x += ' changed in Nested';
    }
    function NoBlockScope() {
        var x = 1;
        assertEqual(1, x);
        if (true) {
            // The scope of variables is the function.
            // This variable x is the same as the one outside the if block.
            var x = 2;
        }
        assertEqual(2, x);
    }
    assertEqual('outer x', x);
    Shadowing();
    assertEqual('outer x', x);
    Nested();
    assertEqual('outer x changed in Nested', x);
    NoBlockScope();
}

// Loops
function Loops() {
    // for loops
    function ForLoops() {
        var i;
        var sum = 0;
        for (i = 0; i < 6; ++i) {
            sum += i;
        }
        assertEqual(15, sum);
    }
    // for..in loops: iterating over indices
    function ForInLoops() {
        var xs = [1, 10, 100, 1000];
        var sum = 0;
        for (var i in xs) {
            sum += xs[i];
        }
        assertEqual(1111, sum);
        var obj = { one: 1, two: 2 };
        var sum = 0;
        for (var p in obj) {
            sum += obj[p];
        }
        assertEqual(3, sum);
        var str = "Coaxlink";
        var sum = "";
        for (var i in str) {
            sum += str[i];
        }
        assertEqual("Coaxlink", sum);
    }
    // for..of loops: iterating over values
    function ForOfLoops() {
        var xs = [1, 10, 100, 1000];
        var sum = 0;
        for (var x of xs) {
            sum += x;
        }
        assertEqual(1111, sum);
        var obj = { one: 1, two: 2 };
        var sum = 0;
        for (var x of obj) {
            sum += x;
        }
        assertEqual(3, sum);
        var str = "Coaxlink";
        var sum = "";
        for (var c of str) {
            sum += c;
        }
        assertEqual("Coaxlink", sum);
    }
    function ContinueAndBreak() {
        var i;
        var sum = 0;
        for (i = 0; i < 100; ++i) {
            if (i === 3) {
                continue;
            } else if (i === 6) {
                break;
            } else {
                sum += i;
            }
        }
        assertEqual(0 + 1 + 2 + 4 + 5, sum);
    }
    ForLoops();
    ForInLoops();
    ForOfLoops();
    ContinueAndBreak();
}

function Exceptions() {
    var x;
    var caught;
    var finallyDone;
    function f(action) {
        x = 0;
        caught = undefined;
        finallyDone = false;
        try {
            x = 1;
            if (action === 'fail') {
                throw action;
            } else if (action === 'return') {
                return;
            }
            x = 2;
        } catch (e) {
            // Executed if a throw statement is executed.
            assertEqual(1, x);
            caught = e;
        } finally {
            // Executed regardless of whether or not a throw statement is
            // executed. Also executed if a return statement causes the
            // function to exit before the end of the try block.
            finallyDone = true;
        }
    }
    f('fail');
    assertEqual(1, x);
    assertEqual('fail', caught);
    assert(finallyDone);
    f('return');
    assertEqual(1, x);
    assert(!caught);
    assert(finallyDone);
    f();
    assertEqual(2, x);
    assert(!caught);
    assert(finallyDone);
}

function CharCode() {
    assertEqual(0x41, "ABC".charCodeAt());
    assertEqual(0x42, "ABC".charCodeAt(1));
    assertEqual(0x43, "ABC".charCodeAt(2));
    assertEqual("A", String.fromCharCode(0x41));
    assertEqual("ABC", String.fromCharCode(0x41, 0x42, 0x43));
}



// Run tests
Variables();
DynamicVariables();
DataTypes();
References();
Operators();
OuterFunction();
Loops();
Exceptions();
CharCode();

function assertEqual(expected, actual) {
    if (expected !== actual) {
        throw 'expected: ' + expected + ', actual: ' + actual;
    }
}

function assert(condition) {
    if (!condition) {
        throw 'failed assertion';
    }
}

6.2 doc/builtins.js

// This file describes the builtins (functions or objects) of Euresys GenApi
// Script. It can be executed by running 'gentl script
// egrabber://doc/builtins.js'.

// The builtin object 'console' contains a function 'log' which can be
// used to output text to the standard output (if available) as well as to
// Memento with the notice verbosity level.
console.log('Hello from ' + module.filename);
console.log('If several arguments are passed,', 'they are joined with spaces');
console.log('All text is sent to both standard output and Memento');

// The builtin object 'memento' contains the following functions: error,
// warning, notice, info, debug, verbose (each corresponding to a different
// verbosity level in Memento). They are similar to console.log, except that
// the text is only sent to Memento.
memento.error('error description');
memento.warning('warning description');
memento.notice('important notification');
memento.info('message');
memento.debug('debug information');
memento.verbose('more debug information');

// For convenience, the object 'console' also contains the same methods as the
// object 'memento'; the functions are similar to their 'memento' counterparts,
// except that they also send text to the standard output if available
console.error('error description');
console.warning('warning description');
console.notice('important notification');
console.info('message');
console.debug('debug information');
console.verbose('more debug information');

// Explicit type conversion/information functions:
console.log('Boolean(0) = ' + Boolean(0));               // false
console.log('Boolean(3) = ' + Boolean(3));               // true
console.log('Number(false) = ' + Number(false));         // 0
console.log('Number(true) = ' + Number(true));           // 1
console.log('Number("3.14") = ' + Number("3.14"));       // 3.14
console.log('Number("0x16") = ' + Number("0x16"));       // 22
console.log('Number("1e-9") = ' + Number("1e-9"));       // 1e-9
console.log('String(false) = ' + String(false));         // "false"
console.log('String(true) = ' + String(true));           // "true"
console.log('String(3.14) = ' + String(3.14));           // "3.14"
console.log('String([1, 2]) = ' + String([1, 2]));       // "1,2"
console.log('isNaN(0/0) = ' + isNaN(0/0));               // true
console.log('isNaN(Infinity) = ' + isNaN(Infinity));     // false
console.log('isRegExp(/re/) = ' + isRegExp(/re/));       // true
console.log('isRegExp("/re/") = ' + isRegExp("/re/"));   // false
console.log('Array.isArray({}) = ' + Array.isArray({})); // false
console.log('Array.isArray([]) = ' + Array.isArray([])); // true

// The builtin object 'Math' contains a few functions:
console.log('Math.floor(3.14) = ' + Math.floor(3.14));
console.log('Math.ceil(3.14) = ' + Math.ceil(3.14));
console.log('Math.abs(-1.5) = ' + Math.abs(1.5));
console.log('Math.pow(2, 5) = ' + Math.pow(2, 5));
console.log('Math.log2(2048) = ' + Math.log2(2048));

// String manipulation
console.log('"Duo & Duo".replace(/Duo/,  "Quad") = "' +
             "Duo & Duo".replace(/Duo/,  "Quad") + '"'); // "Quad & Duo"
console.log('"Duo & Duo".replace(/Duo/g, "Quad") = "' +
             "Duo & Duo".replace(/Duo/g, "Quad") + '"'); // "Quad & Quad"
console.log('"Hello, Coaxlink".toLowerCase() = "' +
             "Hello, Coaxlink".toLowerCase() + '"');     // "hello, coaxlink"
console.log('"Coaxlink Quad G3".includes("Quad") = ' +
             "Coaxlink Quad G3".includes("Quad"));       // true
console.log('"Coaxlink Quad".includes("G3") = ' +
             "Coaxlink Quad".includes("G3"));            // false
console.log('"Coaxlink Quad G3".split(" ") = [' +
             "Coaxlink Quad G3".split(" ") + ']');       // [Coaxlink,Quad,G3]
console.log('"Coaxlink Quad G3".split("Quad") = [' +
             "Coaxlink Quad G3".split("Quad") + ']');    // [Coaxlink , G3]
console.log('["Mono", "Duo", "Quad"].join() = "' +
             ["Mono", "Duo", "Quad"].join() + '"');      // "Mono,Duo,Quad"
console.log('["Mono", "Duo", "Quad"].join(" & ") = "' +
             ["Mono", "Duo", "Quad"].join(" & ") + '"'); // "Mono & Duo & Quad"

// Utility functions
console.log('random(0,1): ' + random(0,1)); // random number between 0 and 1
sleep(0.5); // pause execution of script for 0.5 second

// The builtin function 'require' loads a script, executes it, and returns
// the value of the special 'module.exports' from that module.
var mod1 = require('./module1.js');
console.log('mod1.description: ' + mod1.description);
console.log('mod1.plus2(3): ' + mod1.plus2(3));
console.log('calling mod1.hello()...');
mod1.hello();

// 'require' can deal with:
// - absolute paths
//   var mod = require('C:\\absolute\\path\\some-module.js');
// - relative paths (paths relative to the current script)
//   var mod = require('./utils/helper.js');
// - egrabber:// paths (paths relative to the directory where coaxlink scripts
//   are installed)
//   var mod = require(egrabber://doc/builtins.js);

6.3 doc/grabbers.js

// This file describes the 'grabbers' object of Euresys GenApi Script. It can
// be executed by running 'gentl script egrabber://doc/grabbers.js'.

// The builtin object 'grabbers' is a list of objects giving access to the
// available GenTL modules/ports.

// In most cases, 'grabbers' contains exactly one element. However, when using
// the 'gentl script' command, 'grabbers' contains the list of all devices.
// This makes it possible to configure several cameras and/or cards.

console.log("grabbers.length:", grabbers.length);

// Each item in 'grabbers' encapsulates all the ports related to one data
// stream:
//  TLPort          | GenTL producer
//  InterfacePort   | Coaxlink card
//  DevicePort      | local device
//  StreamPort      | data stream
//  RemotePort      | camera (if available)

var PortNames = ['TLPort', 'InterfacePort', 'DevicePort', 'StreamPort',
                 'RemotePort'];

// Ports are objects which provide the following textual information:
//  name            | one of PortNames
//  tag             | port handle type and value (as shown in memento traces)

for (var i in grabbers) {
    var g = grabbers[i];
    console.log('- grabbers[' + i + ']');
    for (var pn of PortNames) {
        var port = g[pn];
        console.log('  - ' + port.name + ' (' + port.tag + ')');
    }
}

// Ports also have the following functions to work on GenICam features:
//  get(f)                 | get value of f
//  set(f,v)               | set value v to f
//  execute(f)             | execute f
//  done(f)                | test if command f is done (execution completed)
//  features([re])         | get list of features [matching~ re]
//  $features([re])        | strict* variant of features([re])
//  featuresOf(c, [re])    | get list of features of category c [matching~ re]
//  $featuresOf(c, [re])   | strict* variant of featuresOf(c, [re])
//  categories([re])       | get list of categories [matching~ re]
//  $categories([re])      | strict* variant of categories([re])
//  categoriesOf(c, [re])  | get list of categories of category c [matching~ re]
//  $categoriesOf(c, [re]) | strict* variant of categoriesOf(c, [re])
//  ee(f,[re])             | get list of enum entries [matching~ re]
//                         | of enumeration f
//  $ee(f,[re])            | strict* variant of ee(f,[re])
//  has(f)                 | test if f exists
//  has(f,v)               | test if f has an enum entry v
//  available(f)           | test if f is available
//  available(f,v)         | test if f has an enum entry v which is available
//  readable(f)            | test if f is readable
//  writeable(f)           | test if f is writeable
//  implemented(f)         | test if f is implemented
//  command(f)             | test if f is a command
//  selectors(f)           | get list of features that act as selectors of f
//  attributes(...)        | extract information from the XML file describing
//                         | the port
//  interfaces(f)          | get list of interfaces of f (e.g. ["IInteger"])
//  source(f)              | get the XML source of f
//  info(f,what)           | get XML information what of f
//  declare(t,f)           | declare a virtual user feature f of type t,
//                         | t can be one of "integer", "float", "string"
//  undeclare(f)           | undeclare (delete) a virtual user feature f
//  declared()             | get list of virtual user features
//
// * by strict we mean that the returned list contains only nodes/values
//   that are available (as dictated by 'pIsAvailable' GenICam node elements)
// ~ the returned list is filtered by regular expression matching

if (grabbers.length) {
    var port = grabbers[0].InterfacePort;
    console.log('Playing with', port.tag);
    // get(f)
    console.log('- InterfaceID: ' + port.get('InterfaceID'));
    // set(f,v)
    port.set('LineSelector', 'TTLIO11');
    // execute(f)
    port.execute('DeviceUpdateList');
    // features(re)
    console.log('- Features matching \'PCIe\':');
    for (var f of port.features('PCIe')) {
        console.log('  - ' + f);
    }
    // $ee(f)
    console.log('- Available enum entries for LineSource:');
    for (var ee of port.$ee('LineSource')) {
        console.log('  - ' + ee);
    }
    for (var ix of [0, 1, 2, 3, 9]) {
        var ee = 'Device' + ix + 'Strobe';
        // has(f, v)
        if (port.has('LineSource', ee)) {
            console.log('- ' + ee + ' exists');
        } else {
            console.log('- ' + ee + ' does not exist');
        }
        // available(f, v)
        if (port.available('LineSource', ee)) {
            console.log('- ' + ee + ' is available');
        } else {
            console.log('- ' + ee + ' is not available');
        }
    }
    // selectors(f)
    console.log('- LineSource feature is selected by',
                port.selectors('LineSource'));
    // attributes()
    console.log('- attributes()');
    var attrs = port.attributes();
    for (var n in attrs) {
        console.log('  - ' + n + ': ' + attrs[n]);
    }
    // attributes(f)
    console.log('- attributes(\'LineFormat\')');
    var attrs = port.attributes('LineFormat');
    for (var n in attrs) {
        console.log('  - ' + n + ': ' + attrs[n]);
    }
    // attributes(f)
    var fmt = port.get('LineFormat');
    console.log('- attributes(\'LineFormat\', \'' + fmt + '\')');
    var attrs = port.attributes('LineFormat', fmt);
    for (var n in attrs) {
        console.log('  - ' + n + ': ' + attrs[n]);
    }
    // optional suffixes to integer or float feature names
    if (port.available('DividerToolSelector') &&
        port.available('DividerToolSelector', 'DIV1')) {
        var feature = 'DividerToolDivisionFactor[DIV1]';
        var suffixes = ['.Min', '.Max', '.Inc', '.Value'];
        console.log('- Accessing ' + suffixes + ' of ' + feature);
        for (var suffix of suffixes) {
            console.log( '  - ' + suffix + ': ' + port.get(feature + suffix));
        }
    }
    var remotePort = grabbers[0].RemotePort;
    if (remotePort) {
        // getReg(f, len)
        // setReg(f,data)
        if (remotePort.available('LUTValueAll')) {
            // get register length
            var len = remotePort.get('LUTValueAll.Length');
            // get first 8 bytes
            if (len >= 8) {
                var data = remotePort.getReg('LUTValueAll', 8);
                for (var ix in data) {
                    console.log(' - LUTValueAll[' + ix + ']: ' + data.charCodeAt(ix));
                }
                // write data
                // remotePort.setReg('LUTValueAll', data);
            }
        }
    }
}

// Camera ports (RemotePort) also have the following functions:
//  brRead(addr)    | read bootstrap register (32-bit big endian)
//  brWrite(addr,v) | write value to bootstrap register (32-bit big endian)

if (grabbers.length) {
    var port = grabbers[0].RemotePort;
    if (port) {
        console.log('Playing with', port.tag);
        var brStandard = 0x00000000;
        var standard = port.brRead(brStandard);
        console.log('Bootstrap register "Standard" is ' + standard);
    }
}

6.4 doc/module1.js

7 eGrabber for MultiCam users

Concepts
MultiCam eGrabber
Board Interface
Channel Device + Data stream
Surface Buffer
Surface cluster (MC_Cluster) Buffers announced to the data stream
- Remote device (camera)
MultiCam parameters GenApi set/get features
- GenApi commands
CAM file Euresys GenApi script
- CallbackOnDemand
Callback functions CallbackSingleThread
- CallbackMultiThread
Initialization
Channel creation
Surface creation (automatic)
Surface creation (manual)
Surface cluster reset
Frame grabber configuration
MultiCam eGrabber
McSetParamStr(H, MC_CamFile, filepath) grabber.runScript(filepath)
- grabber.runScript(script)
McSetParamInt(H, id, value) or McSetParamNmInt(H, name, value) grabber.setInteger<M>(name, value)
McSetParamFloat(H, id, value) or McSetParamNmFloat(H, name, value) grabber.setFloat<M>(name, value)
McSetParamStr(H, id, value) or McSetParamNmStr(H, name, value) grabber.setString<M>(name, value)

where H is a MC_HANDLE (the global MC_CONFIGURATION handle, a board handle, or a channel handle), and M specifies the target module (either SystemModule, InterfaceModule, DeviceModule, or StreamModule).

Camera configuration
MultiCam eGrabber
- grabber.runScript(filepath)
- grabber.runScript(script)
- grabber.setInteger<RemoteModule>(name, value), grabber.setFloat<RemoteModule>(name, value), or grabber.setString<RemoteModule>(name, value)
Script files
Acquisition start/stop
Synchronous (blocking) buffer reception
Callbacks
Synchronous (blocking) event handling

8 .NET assembly

eGrabber can be used in .NET languages (C#, VB.NET, etc.) via two .NET assemblies named EGrabber.NETFramework.dll and EGrabber.NET.dll.

8.1 A first example

This example creates a grabber and displays basic information about the interface, device, and remote device modules it contains. This is the C# version of the first C++ eGrabber example:

  1. Create an EGenTL object. This will locate, open, and initialize the GenTL producer (e.g., coaxlink.cti).

  2. Create an EGrabber object. The constructor needs the gentl object we’ve just created. It also takes as optional arguments the indices of the interface and device to use.

  3. Use GenApi to find out the ID of the Coaxlink card. We want an answer from the interface module, so the Get is done on grabber.Interface. Notice the <string> to indicate that we want to query the GenICam IString interface of the feature and we want to get back a string value.

  4. Similarly, find out the ID of the device. This time, we use grabber.Device to target the device module.

  5. Finally, read the camera resolution. This time, we use grabber.Remote since the values must be retrieved from the camera. Notice the <ulong> to indicate that we want to query the GenICam IInteger interface of the feature and we want to get back a ulong value.

  6. eGrabber uses exceptions to report errors, so we wrap our code inside a try ... catch block.

Example of program output:

Interface:    PC1633 - Coaxlink Quad G3 (2-camera) - KQG00014
Device:       Device0
Resolution:   4096x4096

8.2 Differences between C++ and .NET eGrabber

eGrabber classes
C++ .NET
EGrabber<> EGrabber
EGrabber<CallbackOnDemand> EGrabber
EGrabber<CallbackSingleThread> -
EGrabber<CallbackMultiThread> -
Buffer Buffer
ScopedBuffer ScopedBuffer used in a using-statement
EGrabber methods
  • In the C++ API, method names are written in camelCase. In C#, method names are written in CamelCase. For example, the C++ reallocBuffers becomes ReallocBuffers in C#.
  • Other differences:

    C++ .NET
    getInfo<MODULE, TYPE>(cmd) Module.GetInfo<TYPE>(cmd)
    getInteger<MODULE>(f) Module.Get<long>(f), Module.Get<int>(f), …
    - Module.Get<bool>(f)
    getFloat<MODULE>(f) Module.Get<double>(f), Module.Get<float>(f)
    getString<MODULE>(f) Module.Get<string>(f)
    getStringList<MODULE>(f) Module.Get<string[]>(f)
    setInteger<MODULE>(f, v) Module.Set<long>(f, v), Module.Set<int>(f, v), …
    - Module.Set<bool>(f, v)
    setFloat<MODULE>(f, v) Module.Set<double>(f, v), Module.Set<float>(f, v)
    setString<MODULE>(f, v) Module.Set<string>(f, v)
    execute<MODULE>(f) Module.Execute(f)
    enableEvent<EVENT_DATA>() EnableEvent(EventType.EVENT_DATA)
    disableEvent<EVENT_DATA>() DisableEvent(EventType.EVENT_DATA)
    where
    • MODULE can be replaced by SystemModule, InterfaceModule, DeviceModule, StreamModule, or RemoteModule;
    • Module can be replaced by System, Interface, Device, Stream, or Remote;
    • EVENT_DATA can be replaced by NewBufferData, DataStreamData, IoToolboxData, CicData, CxpInterfaceData, DeviceErrorData, CxpDeviceData, or RemoteDeviceData.
Callbacks

In .NET, callbacks are defined as methods with two arguments, the grabber and the actual event data:

A complete example is given in the next section.

8.3 Processing events using callbacks

This program displays basic information about CIC events generated by a grabber:

  1. Run a config.js script which should:

    • properly configure the camera and frame grabber;
    • enable notifications for CIC events.
  2. Register the callback function for handling the CIC events.

  3. The CIC event callback has two arguments: the grabber that got the event, and the actual event data.

  4. In the body of the callback function, simply display basic information about the event.

  5. Enable CicData events on the grabber.

  6. Start the grabber and enter an infinite loop to process any incoming event. CIC events will be notified in the same thread.

Example of program output:

timestamp: 2790824897 us, EVENT_DATA_NUMID_CIC_CAMERA_TRIGGER_RISING_EDGE
timestamp: 2790824897 us, EVENT_DATA_NUMID_CIC_STROBE_RISING_EDGE
timestamp: 2790824902 us, EVENT_DATA_NUMID_CIC_CXP_TRIGGER_ACK
timestamp: 2790825897 us, EVENT_DATA_NUMID_CIC_STROBE_FALLING_EDGE
timestamp: 2790830397 us, EVENT_DATA_NUMID_CIC_CAMERA_TRIGGER_FALLING_EDGE
timestamp: 2790830401 us, EVENT_DATA_NUMID_CIC_CXP_TRIGGER_ACK
timestamp: 2790842190 us, EVENT_DATA_NUMID_CIC_ALLOW_NEXT_CYCLE
timestamp: 2790842190 us, EVENT_DATA_NUMID_CIC_CAMERA_TRIGGER_RISING_EDGE
timestamp: 2790842191 us, EVENT_DATA_NUMID_CIC_STROBE_RISING_EDGE
timestamp: 2790842195 us, EVENT_DATA_NUMID_CIC_CXP_TRIGGER_ACK

9 Python

eGrabber can also be used in Python.

9.1 Installation

The Python bindings for eGrabber are provided in a Pyhon wheel installation package (a .whl file) located in the python subdirectory of the eGrabber installation directory4. Depending on your Python setup, installation can be as easy as:

python -m pip install <PATH_TO_EGRABBER_WHL>

9.2 A first example

This example creates a grabber and displays basic information about the interface, device, and remote device modules it contains. This is the Python version of the first C++ eGrabber example:

  1. Import the egrabber module.

  2. Create an EGenTL object. This will locate, open, and initialize the GenTL producer (e.g., coaxlink.cti).

  3. Create an EGrabber object. The constructor needs the gentl object created in step 2. It also takes as optional arguments the indices of the interface and device to use.

  4. Use GenApi to find out the ID of the Coaxlink card. We want an answer from the interface module, so the get is done on grabber.interface.

  5. Similarly, find out the ID of the device. This time, we use grabber.device to target the device module.

  6. Finally, read the camera resolution. This time, we use grabber.remote since the values must be retrieved from the camera.

  7. egrabber uses exceptions to report errors, so we wrap our code inside a try: ... except: ... block.

Example of program output:

Interface:    PC1633 - Coaxlink Quad G3 (2-camera) - KQG00014
Device:       Device0
Resolution:   4096x4096

9.3 Differences between C++ and Python eGrabber

eGrabber classes
C++ Python
EGrabber<> EGrabber
EGrabber<CallbackOnDemand> EGrabber
EGrabber<CallbackSingleThread> -
EGrabber<CallbackMultiThread> -
Buffer Buffer
ScopedBuffer Buffer used in a with-block
EGrabber methods
  • In the C++ API, method names are written in camelCase. In Python, method names are written in lower_case. For example, the C++ reallocBuffers becomes realloc_buffers in Python.
  • Other differences:

    C++ Python
    getInteger<MODULE>(f) module.get(f, dtype=int)
    getFloat<MODULE>(f) module.get(f, dtype=float)
    getString<MODULE>(f) module.get(f, dtype=str)
    getStringList<MODULE>(f) module.get(f, dtype=list)
    - module.get(f, dtype=bool)
    - module.get(f)
    setInteger<MODULE>(f, v) module.set(f, v)
    setFloat<MODULE>(f, v) module.set(f, v)
    setString<MODULE>(f, v) module.set(f, v)
    execute<MODULE>(f) module.execute(f)

    where MODULE can be replaced by SystemModule, InterfaceModule, DeviceModule, StreamModule, or RemoteModule; and module can be replaced by system, interface, device, stream, or remote.

10 Sample programs

Sample programs for eGrabber are provided in a dedicated package named egrabber-<OS>-sample-programs-<YY.MM.RE.BU>.<EXT> where <OS> is the operating system (linux, macos, or win) and <YY.MM.RE.BU> is the version number of the package.

These sample programs will use Coaxlink by default. To use them with Grablink:

Similarly, to use Gigelink (which requires a license5):

Sample program Description Language OS
cpp/egrabber Collection of code snippets for eGrabber C++ Windows, Linux, macOS
cpp/live Win32 application showing image acquisition and display C++ Windows
cpp/egrabber-mfc MFC application showing image acquisition and display C++ Windows
cpp/sdl2-live SDL2 application showing image acquisition and display C++ Linux, macOS
cpp/amdDirectGMA OpenGL application showing image acquisition, direct transfer to AMD GPU memory, and display C++ Windows
cpp/nvidia/egrabber-cuda OpenGL console application showing image acquisition with eGrabber and processing with CUDA (on Nvidia GPU) C++ Windows, Linux
cpp/ffcWizard Console application showing how to compute coefficients for the Coaxlink FFC (flat-field correction) C++ Windows, Linux, macOS
cpp/exif Collection of sample programs showing how to use the Coaxlink Quad CXP-12 JPEG and how to embed metadata in EXIF files C++ Windows, Linux, macOS
cpp/GrablinkSerialCommunication Simple application demonstrating Camera Link serial communication through the clseregl library on a Grablink Duo board C++ Windows
cs/egrabber Console application showing how to use eGrabber and callbacks in C# C# Windows
cs/grabn Console application showing image acquisition C# Windows
cs/grabn.NET .NET console application showing image acquisition C# Windows
cs/live Windows Forms application showing image acquisition and display C# Windows
cs/egrabber-wpf WPF application showing image acquisition and display C# Windows
cs/recorder Collection of sample programs eGrabber Recorder C# sample programs for eGrabber Recorder C# Windows
cs/GrablinkSerialCommunication Simple application demonstrating Camera Link serial communication through the clseregl library on a Grablink Duo board C# Windows
python/sample* Collection of sample programs eGrabber Python sample programs for eGrabber Python Windows, Linux, macOS
python/recorder/sample* Collection of sample programs eGrabber Recorder Python sample programs for eGrabber Recorder Python Windows
python/live* Collection of extra sample programs eGrabber Python sample programs for eGrabber showing how to process acquired data with numpy, opencv, Pillow, etc. Python Windows, Linux, macOS
vb/grabn Console application showing image acquisition VB.NET Windows
vb/live Windows Forms application showing image acquisition and display VB.NET Windows
Additional files Description Language OS
tools/stripeGeometry.py Python script showing the effect of the image transfer settings: StripeArrangement, StripeHeight, StripePitch, StripeOffset, and BlockHeight Python Windows, Linux, macOS
LICENSE License text for eGrabber sample programs All Windows, Linux, macOS

10.1 eGrabber C++ code snippets

cpp/egrabber contains the following code snippets:

Snippet Description
100-grabn Simple Grab N frames using ScopedBuffer class
101-singleframe Single frame grabbing using ScopedBuffer class
102-action-grab Single frame triggered by an action
105-area-scan-grabn Set image size and Grab N frames (area-scan)
106-line-scan-grabn Set image size and Grab N frames (line-scan)
110-get-string-list Basic usage of EGrabber method getStringList
120-converter Measure FormatConverter speed
130-using-buffer Simple Grab N frames using Buffer class
140-genapi-command Queries on GenApi commands
150-discover Discover and create eGrabbers or cameras with EGrabberDiscovery
200-grabn-callbacks Grab N frames and get DataStream events with callbacks
201-grabn-pop-oneof Grab N frames and get DataStream events using pop(OneOf<>)
210-show-all-grabbers Show available grabbers
211-show-all-grabbers-ro Show available grabbers (devices are opened with DEVICE_ACCESS_READONLY)
212-create-all-grabbers Create available grabbers
213-egrabbers Use available grabbers with EGrabbers
220-get-announced-handles Get info and handles of announced buffers
221-queue-buffer-ranges Create and use 2 sets of buffers configured differently
230-script-vars Pass data between native code and Euresys script
231-script-var Create and use virtual features from native code and Euresys scripts
240-user-memory Grab into user allocated buffer
250-using-lut Configure and enable the LUT processor
260-egrabber-recorder Write/Read buffers to/from a Recorder container
261-recorder-parameters Show Recorder parameters
300-events-mt-cic CIC events on EGrabber Multi-Thread Configuration
301-events-st-all All events on EGrabber Single-Thread Configuration
302-cxp-connector-detection Show CoaXPress events related to connection and device discovery
310-high-frame-rate Grab in high frame rate mode for 10 seconds
311-high-frame-rate Process images as soon as available in high frame rate mode for 10 seconds
312-part-timestamps Show timestamp of each buffer part in HFR mode
320-cl-serial-cli Command line interface for serial communication with a Camera Link camera
330-metadata-insertion Insert buffer and line metadata into a buffer and get them
500-grabn-cuda-process Grab N frames and process them with cuda operations
501-all-grabbers-cuda-process Use all available interfaces and devices to grab N frames and process them with cuda operation
502-grabn-cuda-copy-and-process Grab N frames, copy the buffers to the cuda device and process them with cuda operations
503-grabn-cuda-rdma-process Grab N frames in the GPU memory with RDMA and process them with cuda operations
600-thread-start-stop-callbacks Perform specific operations on a callback thread when it starts/stops
610-line-scan-array Array of (contiguous) buffers on Line-Scan with EGrabber Single-Thread
620-multiple-camera Acquire data from all cameras
630-sublink Merge buffers from 2 Sub-Link grabbers on the same PC
640-mitsubishi-kd6r807cx Merge buffers from 2 grabbers connected to a Mitsubishi KD6R807CX
641-mitsubishi-kd6r807cx Merge buffers from 2 grabbers connected to a Mitsubishi KD6R807CX
650-multistream Acquire data from 4 data streams on the same device
660-phantom Merge buffers from grabbers connected to a Vision Research Phantom S990 (16 CXP6)
700-memento Generate memento waves

10.2 EXIF sample programs

cpp/exif contains the following samples:

Sample Description
100-jpeg-exif Acquire data from 4 JPEG encoded data streams and produce EXIF files
200-jpeg-preview-exif Acquire data from 4 Preview and 4 JPEG encoded data streams and produce EXIF files with thumbnails

10.3 eGrabber Python sample programs

Sample Description
live Image acquisition and display
live-capture-opencv Acquire and convert frames to RGB8 to produce an avi file with opencv and numpy
live-numpy-opencv Create numpy arrays from acquired Mono8 data, transpose arrays and use opencv to show images
live-tkinter-pillow Simple tkinter application showing acquired data processed by a Pillow contour filter
sample100 Simple Grab N using ‘with Buffer’
sample120 Python version of the C++ 120-converter eGrabber sample program
sample130 Simple Grab N with manual buffer management
sample140 Queries on GenApi commands
sample150 Discover and create eGrabbers or cameras with EGrabberDiscovery
sample200 Grab N frames and get DataStream events with callbacks
sample201 Grab N frames and get DataStream events with pop_one_of
sample210 Show available grabbers
sample240 Grab into user allocated buffer
sample310 Grab in high frame rate mode for 10 seconds

10.4 eGrabber Recorder Python sample programs

Sample Description
sample260 Write/Read buffers to/from a Recorder container
sample261 Show Recorder parameters
sample262 Export images from the container created by sample260.py to an MKV file, and then use opencv to read the MKV file and display the images
sample360 Write to a Recorder container using EGrabber callback

10.5 eGrabber Recorder C# sample programs

Sample Description
260-egrabber-recorder Write/Read buffers to/from a Recorder container
261-recorder-parameters Show Recorder parameters

11 Definitions

11.1 Acronyms

CIC
Camera and Illumination Controller
CTI
Common Transport Interface
CXP
CoaXPress
EMVA
European Machine Vision Association
PFNC
Pixel Format Naming Convention
SFNC
Standard Features Naming Convention

11.2 Glossary

Buffer module

GenTL module that represents a memory buffer. Buffers must be announced to the data stream that will fill them with image data.

Callback model

Defines when and where (in which thread) callback functions are executed.

One of CallbackOnDemand, CallbackSingleThread, CallbackMultiThread.

Camera and illumination controller

Part of Coaxlink card that controls a camera and its associated illumination devices.

Hosted in the device module.

CoaXPress

High speed digital interface standard that allows the transmission of data from a device (e.g., a camera) to a host (e.g., a frame grabber inside a computer) over one or several coaxial cables.

Common transport interface

GenTL producer.

Data stream module

GenTL module that handles buffers.

Device module

GenTL module that contains the frame grabber settings relating to the camera.

Parent of the data stream module.

Sibling of the remote device.

GenApi

The GenICam standard that deals with camera and frame grabber configuration.

GenApi feature

Camera or frame grabber feature, defined in a register description.

Either a set/get parameter, or a command with side effects.

GenICam

Set of EMVA standards. It consists of GenApi, GenTL, the SFNC and the PFNC.

GenTL

The GenICam standard that deals with data transport. TL stands for Transport Layer.

GenTL producer

Software library that implements the GenTL API.

File with the cti extension (e.g., coaxlink.cti, grablink.cti, or gigelink.cti).

Info command

Numerical identifier used to query a specific piece of information from a GenTL module. Info commands are defined either in the standard GenTL header file, or in a vendor-specific header file (e.g., info commands specific to Coaxlink, Grablink, and Gigelink are defined in include/GenTL_v1_5_EuresysCustom.h).

Interface module

GenTL module that represents a frame grabber.

Parent of the device module.

I/O toolbox

Part of Coaxlink card that controls digital I/O lines and implements tools such as rate converters, delay lines, etc.

Hosted in the interface module.

Multi-bank camera

Camera composed of several independent CoaXPress sub-Devices usually organized as a master sub-Device and one or more slave sub-Devices.

Register description

XML file mapping low-level hardware registers to camera or frame grabber features.

Remote device

Camera connected to a frame grabber.

The term remote is used to distinguish this from the GenTL device module.

System

GenTL module that represents the GenTL producer.

Also known as TLSystem.

Parent of the interface module.

Timestamp

The time at which an event occurs.

For Coaxlink, Grablink, and Gigelink, timestamps are always 64-bit integers and are expressed as the number of microseconds that have elapsed since the computer was started.


  1. On Windows, the application must be linked with Kernel32.lib.

    On Linux, the application must be linked with -ldl and -lpthread.

  2. On Windows, the application must be linked with Kernel32.lib.

    On Linux, the application must be linked with -ldl and -lpthread.

  3. Only for the multi-bank cameras supported by the eGrabber package.

  4. On Windows, eGrabber is typically installed in C:\Program Files\Euresys\eGrabber.

    On Linux, eGrabber is installed in /opt/euresys/egrabber.

    On macOS, eGrabber is installed in /usr/local/opt/euresys/egrabber.

  5. 4400 eGrabber Gigelink or 4400-EV eGrabber Gigelink 30-day evaluation license.


© EURESYS S.A. - Subject to change without notice.