siso_genicam#
To use the library, the include file siso_genicam.h
should be added to the source code.
#include <siso_genicam.h>
Additionally, siso_genicam.lib
should be added to your Microsoft Visual Studio Project, or libsiso_genicam.so
to your Linux project. If you use CMake, the package name is SisoGenicam and the libraries are stored in the variable ${SisoGenicam_LIBRARIES}
. See Prerequisites for more details on projects and how to use CMake.
The library is always used together with the frame grabber library fglib5, see The Frame Grabber Library fglib5 for details on how to add the library fglib5 to your project.
Error Handling in the Camera Control Library#
const char * Sgc_getErrorDescription(
int result);
Most functions of the API return an int
result code. If the function call was executed successfully, the return value will be SGC_OK
or for some functions a value equal or greater than zero. A negative value denotes an error condition in most cases. The error codes are defined in the header file siso_genicam_error.h
, which is included automatically in the file siso_genicam.h
.
The function Sgc_getErrorDescription()
can be used to get a string representation of a specific result code.
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.
Camera Control Library Initialization#
int Sgc_initBoard(
Fg_Struct * fg,
unsigned int flags,
SgcBoardHandle ** board);
void Sgc_freeBoard(
SgcBoardHandle * board);
To start working with the siso_genicam library, the frame grabbers have to be initialized first as described in The Frame Grabber Library fglib5. Next, the function Sgc_initBoard()
should be called for every frame grabber that the application uses. The parameter flags
is reserved for future extensions and the application should always pass 0. If the initialization was successful, the board handle is stored to the variable passed in parameter board
.
If the application is done using the siso_genicam library, all board handles obtained successfully should be released by calling Sgc_freeBoard()
for each of them.
The following example shows how to get a board handle to start using the library:
SgcBoardHandle * board = nullptr;
int result = Sgc_initBoard(fg, 0, &board);
if (result != SGC_OK) {
// handle error ...
}
// use board, start discovery ...
Sgc_freeBoard(board);
Camera Discovery#
int Sgc_scanPorts(
SgcBoardHandle * board,
unsigned int portMask,
int timeout,
int speed);
int Sgc_getCameraCount(
SgcBoardHandle * board);
int Sgc_getCameraByIndex(
SgcBoardHandle * board,
unsigned int index,
SgcCameraHandle ** camera);
int Sgc_getCamera(
SgcBoardHandle * board,
unsigned int port,
SgcCameraHandle ** camera);
Info
In version 5.9 of the Framegrabber API the behavior of the function Sgc_scanPorts()
and the return value of the function Sgc_getCameraCount()
both were changed.
Before the application can work with cameras, they have to be discovered. The discovery procedure scans the physical ports of a frame grabber for connections. When connections are detected on more than one ports, the next step is to find out how many cameras are connected and how many physical connections each camera is using. The physical connections and the properties thereof, like the transmission speed used, is referred to as link. A camera supports one or more link configurations. For example, a CoaXPress camera might support various link configurations. For example, a camera which has four physical ports might support configurations with one, two or four links using a subset of the CoaXPress 2.0 link speeds: 3.125, 5, 6.25, 10 and 12.5 Gbit/s. So this camera would support a total of 15 different link configurations.
To start the camera discovery procedure, the function Sgc_scanPorts()
should be called. The function expects the board handle from a previous call to Sgc_initBoard()
in the parameter board
, a bit mask specifying the physical ports for which the discovery should be performed in the parameter portMask
and the time in milliseconds to wait for at least one camera to be discovered in the parameter timeout
. The parameter speed
should be LINK_SPEED_NONE
in most cases. It can optionally be used to give the discovery a hint at which link speed a camera is expected to be discovered.
In an application that uses a single process for one or more frame grabbers, the port mask should always be 0xf
. This way, all ports of the frame grabber will be scanned for cameras. If an application uses multiple processes for a frame grabber, read chapter Support for Camera Discovery from Multiple Processes.
After a camera was discovered, the final link configuration is negotiated according to the applet used and the camera default configuration. It is helpful to know the limitations of this process to understand the state in which a discovered camera is presented to the application. The discovery algorithm doesn't use the GenICam XML provided by the camera. Instead, the default camera configuration determines the maximum link speed and number of links that can be used by the discovery procedure for the camera. For some cameras, the default configuration isn't the highest link speed or the maximum number of links the camera can use! The applet layout on the other hand determines the maximum number of cameras and for each camera, the maximum number of links that can be used by the discovery procedure for the applet. The discovery algorithm tries to best match the cameras found on one hand, and the applet layout on the other. Consult the applet documentation and the documentation of the camera to find the applet that best matches the camera setup and features which you intend to use.
(Also see chapter Understanding Image Data Flow for a diagram illustrating the applet layout. For each camera attached to the frame grabber, the applet needs a corresponding camera operator. The number of camera operators is a fixed property of each applet. You might also want to review the comment at the end of chapter Frame Grabber Initialization of part The Frame Grabber Library fglib5 to understand how to choose an applet for the cameras attached to a frame grabber.)
If the call to Sgc_scanPorts()
returns SGC_OK
, then at least one camera was found in the specified time and the applet and the cameras could be matched. If the return value is ERR_SGC_TOPOLOGY_MISMATCH
, then at least one camera was found, but the applet and the cameras could not be matched, and while communication with the camera is still possible, image data can't be transferred correctly from the camera to the applet.
After a successful call to Sgc_scanPorts()
, the function Sgc_getCameraCount()
can be called to request the number of cameras found and use either Sgc_getCameraByIndex()
or Sgc_getCamera()
to get a camera handle. Both functions work in similar ways and expect three parameters. The first parameter is the board handle. For Sgc_getCameraByIndex()
, the second parameter identifies the camera by a number between 0 and the number of cameras as reported by Sgc_getCameraCount()
minus one. So the function can be used to conveniently iterate over all cameras discovered, but the order in which the cameras are discovered and thus the index can change between two calls to Sgc_scanPorts()
. For Sgc_getCamera()
, the second parameter identifies the camera by the physical port the camera is connected to. Physical ports are numbered starting with 0 up to the physical port count of the frame grabber used minus one. If the same physical cabling is always used, this function can be used to avoid the ambiguities mentioned for Sgc_getCameraByIndex()
. The third parameter is a pointer to a variable in which to store the camera handle.
The following example shows how to look for a camera for a maximum of 10 seconds and then get the corresponding camera handle:
const unsigned int portMask = 0xf;
const int timeoutInMs = 10000;
int result = Sgc_scanPorts(board, portMask, timeoutInMs, LINK_SPEED_NONE);
if (result != SGC_OK) {
// handle error ...
}
SgcCameraHandle * camera = nullptr;
result = Sgc_getCameraByIndex(board, index, &camera);
if (result == SGC_OK) {
// use camera ...
}
The simple approach outlined in the example above works only in single camera setup.
For applications working with more than one camera, the recommended way of handling discovery is to register a callback function as discussed in chapter Registering a Callback Function for Discovery Events, and then call Sgc_scanPorts()
with a timeout of 0 to start the discovery process. From within the callback handler, the application can for example start separate acquisition threads when a new camera is discovered.
Alternatively, the application can call Sgc_scanPorts()
repeatedly until all cameras expected are found. The following example waits for four cameras to be discovered, before continuing processing:
const unsigned int portMask = 0xf;
const int timeoutInSeconds = 10;
const int numberOfCamerasExpected = 4;
int result = SGC_OK;
int numberOfCamerasFound = 0;
auto start = std::chrono::steady_clock::now();
do {
result = Sgc_scanPorts(board, portMask, 1000, LINK_SPEED_NONE);
if (result == SGC_OK) {
numberOfCamerasFound = Sgc_getCameraCount(board);
}
} while ((numberOfCamerasFound < numberOfCamerasExpected) && (std::chrono::steady_clock::now() - start < std::chrono::seconds(timeoutInSeconds)));
if (numberOfCamerasFound < numberOfCamerasExpected) {
// handle error ...
}
// use cameras ...
Restarting the Camera Discovery#
int Sgc_scanPortsEx(
SgcBoardHandle * board,
unsigned int portMask,
int timeout,
int speed,
unsigned int flags);
Info
The default behavior of the function Sgc_scanPortsEx()
was changed in version 5.9 of the Framegrabber API.
The functions Sgc_scanPorts()
and Sgc_scanPortsEx()
when called passing 0 in the parameter flags
both will start a background thread which performs the discovery procedure and then synchronize against the thread to check if any camera was found during the time specified in the call. Any subsequent call to the function will only perform the synchronization to check for cameras. To perform a full restart of the camera discovery procedure, Sgc_scanPortsEx()
can be called using SGC_SP_FLAG_RESET_DISCOVERY_INFOS
in the parameter flags
.
Working with Camera Features#
After a camera has been discovered, the application can start using the camera features. These features, the camera parameters and commands, are defined in an XML document that can be provided by the camera for download through a standardize feature interface, or through other means by the camera manufacturer (for example, some camera manufacturers might offer a website where XML documents can be downloaded). The structure of the XML document is defined in the GenICam standard. To learn more about GenICam visit the GenICam websie of the European Machine Vision Association. To learn more about the features which the camera provides, refer to the camera documentation.
The GenICam standard also defines a way to control the camera features through GenApi. Chapter Using the GenICam Reference Implementation will discuss connecting the siso_genicam library with GenApi.
In this chapter, a set of functions is documented which allows using the camera features directly from the Framegrabber API, wrapping the GenApi for easier use. While the API described can be used for getting and setting camera parameters and executing commands when those features are known, advanced functionality of the XML isn't supported and requires the use of GenApi by the application.
Camera Connection#
int Sgc_connectCamera(
SgcCameraHandle * camera);
int Sgc_connectCameraWithExternalXml(
SgcCameraHandle * cameraHandle,
const char * file);
int Sgc_disconnectCamera(
SgcCameraHandle * camera);
In the discovery procedure, the physical connection to the discovered cameras has been established. To use the camera features, a logical connection has to be established. This logical connection will be established by reading the GenICam XML and preparing the GenApi environment which is used internally by the Framegrabber SDK. To establish a logical connection using the XML provided by the camera, the function Sgc_connectCamera()
should be called. If an XML file downloaded from the manufacturer website or obtained by other means should be used, the function Sgc_connectCameraWithExternalXml()
can be used. Both functions expect the camera handle in the parameter camera
. The function Sgc_connectCameraWithExternalXml()
expects a path to the XML file in the parameter file
in addition.
When the application is done using camera features, the logical connection can be closed by calling Sgc_disconnectCamera()
.
Getting and Setting Camera Features#
int Sgc_getIntegerValue(
SgcCameraHandle * camera,
const char * feature,
int64_t * value);
int Sgc_getBooleanValue(
SgcCameraHandle * camera,
const char * feature,
unsigned int * value);
int Sgc_getFloatValue(
SgcCameraHandle * camera,
const char * feature,
double * value);
int Sgc_getStringValue(
SgcCameraHandle * camera,
const char * feature,
const char ** value);
int Sgc_getEnumerationValue(
SgcCameraHandle * camera,
const char * feature,
int64_t * value);
int Sgc_getEnumerationValueAsString(
SgcCameraHandle * camera,
const char * feature,
const char ** value);
int Sgc_clearStringCache(
SgcCameraHandle * camera);
int Sgc_setIntegerValue(
SgcCameraHandle * camera,
const char * feature,
int64_t value);
int Sgc_setBooleanValue(
SgcCameraHandle * camera,
const char * feature,
unsigned int value);
int Sgc_setFloatValue(
SgcCameraHandle * camera,
const char * feature,
double value);
int Sgc_setStringValue(
SgcCameraHandle * camera,
const char * feature,
const char * value);
int Sgc_setEnumerationValue(
SgcCameraHandle * camera,
const char * feature,
const char * value);
To request values of camera parameters with a numerical representation, the functions Sgc_getIntegerValue()
, Sgc_getBooleanValue()
, Sgc_getFloatValue()
and Sgc_getEnumerationValue()
can be used. These functions expect the camera handle in the parameter camera
, the feature name in the parameter feature
and a pointer to a variable of the corresponding type to which the value will be stored in the parameter value
.
The functions to request values of camera parameters with a string representation, Sgc_getStringValue()
and Sgc_getEnumerationValueAsString()
, expect a pointer to a variable of type char *
in the parameter value
. In this variable, a pointer to the string value will be stored.
The string value will be allocated and stored in an internal cache. The cache will grow every time the application requests string values and there is no direct way to release a single string value from the cache. However, the cache can be cleared by calling Sgc_clearStringCache()
. This will release and invalidate all previously allocated string values.
To set values, the functions Sgc_setIntegerValue()
, Sgc_setBooleanValue()
, Sgc_setFloatValue()
, Sgc_setStringValue()
and Sgc_setEnumerationValue()
can be called. These functions expect the camera handle in the parameter camera
, the feature name in the parameter feature
and the value in the parameter value
.
The following example shows how to connect a camera with the camera XML and set the width and height of the image to be transferred:
int result = Sgc_connectCamera(camera);
if (result == SGC_OK) {
result = Sgc_setIntegerValue(camera, "Width", 1920);
}
if (result == SGC_OK) {
result = Sgc_setIntegerValue(camera, "Height", 1080);
}
Executing Camera Commands#
int Sgc_executeCommand(
SgcCameraHandle * camera,
const char* feature);
int Sgc_isCommandDone(
SgcCameraHandle * camera,
const char* feature,
unsigned int * value);
int Sgc_startAcquisition(
SgcCameraHandle * camera,
unsigned int start);
int Sgc_stopAcquisition(
SgcCameraHandle * camera,
unsigned int stop);
To execute a command, the function Sgc_executeCommand()
can be called. The function expects the camera handle in the parameter camera
and the feature name in the parameter feature
. The function Sgc_isCommandDone()
can be used to check if a command has finished executing. It expects the camera handle in the parameter camera
, the feature name in the parameter feature
and a pointer to a variable of type unsigned int
in the parameter value
. If the command has finished executing, the value 1 will be stored in the variable, if the command is still executing, the value 0 will be stored.
The functions Sgc_startAcquisition()
and Sgc_stopAcquisition()
are shortcuts for the standard features AcquisitionStart
and AcquisitionStop
. The function will only execute if the parameters start
or stop
evaluate to true
:
int Sgc_startAcquisition(SgcCameraHandle * camera, unsigned int start)
{
int result = SGC_OK;
if (start) {
result = Sgc_executeCommand(camera, "AcquisitionStart");
}
return result;
}
Camera Information#
int Sgc_getCameraPropertyWithType(
SgcCameraHandle * camera,
const char * property,
void * value,
unsigned int * type,
void * additionalValue);
int Sgc_updateCameraInfos(
SgcCameraHandle * camera);
Info
In version 5.9 of the Framegrabber API the information available through the function Sgc_getCameraPropertyWithType()
was extended and the function Sgc_updateCameraInfos()
was added.
After the physical connection to a camera has been established in the discovery procedure, various information about the camera and the connection is available for requesting.
The function Sgc_getCameraPropertyWithType()
can be called to request information about the camera connection. The function expects the camera handle in the parameter camera
, the connection property in the parameter property
, a pointer to a variable in which the value will be stored, a pointer to a variable of type unsigned int
which must be initialized to reflect the type of the property. The parameter additionalValue
is required for some properties as documented below:
Property | Description | Type |
---|---|---|
CAM_PROP_MASTERID | Master id of the cameraa | uint32_t |
CAM_PROP_NROFLINKS | Number of physical links used for image transfer | uint32_t |
CAM_PROP_LINKSPEED | Current link speed (in Mbit/s) | uint32_t |
CAM_PROP_DISCOVERYSPEED | Link speed at which the camera was discovered (in Mbit/s) | uint32_t |
CAM_PROP_FGPORT | Physical port to which a link is connected (additionalValue: link index) | uint32_t |
CAM_PROP_MASTERPORT | Physical port to which the master link is connected (same as FGPORT for link 0) | uint32_t |
CAM_PROP_APPLETMASTERID | Master id of the applet camera porta | uint32_t |
CAM_PROP_APPLETOPERATORINDEX | Index of the applet camera operator | uint32_t |
CAM_PROP_APPLETOPERATORSIZE | Number of links of the applet camera operator | uint32_t |
CAM_PROP_APPLETPORT | Logical port to which a link is connected (additionalValue: link index) | uint32_t |
CAM_PROP_CONTROLPACKETSIZE | Maximum size of control packets | uint32_t |
CAM_PROP_STREAMPACKETSIZE | Maximum size of stream packets | uint32_t |
CAM_PROP_ISCONNECTED | 0: no physical connection 1: physical connection established | uint32_t |
CAM_PROP_ISGENICAMCONNECTED | 0: no logical connection 1: logical connection established | uint32_t |
CAM_PROP_ISTRANSCEIVERLOCKED | 0: too many transmission errors 1: transmission is stable | uint32_t |
CAM_PROP_XML_DATA | Camera XML data as a zero-terminated string (additionalValue: buffer size) | char * |
CAM_PROP_VENDOR_NAME | Camera vendor name as a zero-terminated string (additionalValue: buffer size) | char * |
CAM_PROP_MODEL_NAME | Camera model name as a zero-terminated string (additionalValue: buffer size) | char * |
CAM_PROP_FAMILY_NAME | Camera family name as a zero-terminated string (additionalValue: buffer size) | char * |
CAM_PROP_VERSION | Camera version as a zero-terminated string (additionalValue: buffer size) | char * |
CAM_PROP_FIRMWARE_VERSION | Camera firmware version as a zero-terminated string (additionalValue: buffer size) | char * |
CAM_PROP_MANUFACTURER_INFO | Camera manufacturer info as a zero-terminated string (additionalValue: buffer size) | char * |
CAM_PROP_SERIAL_NUMBER | Camera serial number as a zero-terminated string (additionalValue: buffer size) | char * |
CAM_PROP_USER_ID | Camera user id as a zero-terminated string (additionalValue: buffer size) | char * |
The list above isn't complete and contains only the properties for the information useful to the application. See the Framegrabber API reference for more use cases of the API.
If a property uses the parameter additionalValue
for the link index, the default link index is 0 if nullptr
is passed to additionalValue
in the call. Link index 0 is the master link of the camera.
The following values should be used to request a value of the corresponding type:
Property Type | C/C++ Type |
---|---|
SGC_PROPERTY_TYPE_UINT | uint32_t |
SGC_PROPERTY_TYPE_DOUBLE | double |
SGC_PROPERTY_TYPE_STRING | char * |
The following example shows how to get the speed at which the camera is connected and the number of links:
unsigned int numberOfLinks = 0;
unsigned int linkSpeed = 0;
int type = SGC_PROPERTY_TYPE_UINT;
int result =
Sgc_getCameraPropertyWithType(camera, CAM_PROP_NROFLINKS,
&linkSpeed, &type, nullptr);
if (result == SGC_OK) {
result =
Sgc_getCameraPropertyWithType(camera, CAM_PROP_LINKSPEED,
&numberOfLinks, &type, nullptr);
}
if (result == SGC_OK) {
std::cout << "Link configuration: " << numberOfLinks
<< " x " << (1e-3 * linkSpeed)
<< " Gbit/s" << std::endl;
}
To request a property of type SGC_PROPERTY_TYPE_STRING
, two calls to Sgc_getCameraPropertyWithType()
are required. Passing nullptr to the parameter value
and a pointer to a variable of type unsigned int
to the parameter additionalValue
, the size of the buffer required for the string including the terminating \0
character can be requested and will be stored in the variable. In a second call, a pointer to the allocated buffer is passed in the parameter value
along the pointer to the variable for the size in the parameter additionalValue
. In case the buffer was allocated with more space than needed, on return the variable for the size of the buffer will contain the length of the data including the terminating \0
character.
The following example shows how to request the device model name using Sgc_getCameraPropertyWithType()
: (The example requires the C++ header file memory
for the std::unique_ptr
type.)
// get size of model name
unsigned int size = 0;
unsigned int propertyType = SGC_PROPERTY_TYPE_STRING;
result =
Sgc_getCameraPropertyWithType(camera, CAM_PROP_MODEL_NAME,
nullptr, &propertyType, &size);
if (result == SGC_OK && size > 0) {
// create buffer and request camera model name
std::unique_ptr<char[]> modelName(new char[size]);
result =
Sgc_getCameraPropertyWithType(camera, CAM_PROP_MODEL_NAME,
modelName.get(), &propertyType, &size);
if (result == SGC_OK) {
std::cout << "Camera model name: " << modelName.get() << std::endl;
}
}
When the application uses camera features which imply a change to the information available through Sgc_getCameraPropertyWithType()
, for example by setting the user id string or updating the camera firmware version, the function Sgc_updateCameraInfos()
should be called to request an update of the data in the Framegrabber API.
Understanding Image Data Flow#
To understand how image data flows from a camera through the applet in the frame grabber into the application memory, let's first look at a simple setup where two cameras are connected to the corresponding camera operators in the applet in a straight-forward way:
The applet in the example could be Acq_DualCXP12Area
, or a similar applet. Image data from the camera with the camera index 0 and master id 1 flows through the pysical ports 0 and 1 of the frame grabber to the camera operator with the operator index 0 and master id 1 before it is processed in an image pipeline specific to the applet and sent to the application memory via the DMA operator with index 0. (Because the master id is based on the port to which the camera or operator is connected, the second camera and operator have a master id of 3, not 2. Their index is 1.)
From the perspective of the application, the important information is on which DMA channel to expect the image data of a camera. For the applet layout in the example only one property of the camera, CAM_PROP_APPLETOPERATORINDEX
, is needed to know the correct DMA channel. All applets which are included in the Framegrabber SDK installation are designed according to this principle. For applets designed with VisualApplets it is strongly recommended to take a similar approach and to document the layout.
Let's look at a more complex example where the cameras are connected with cables crossed:
For the image transfer to work correctly, the discovery procedure has to connect the physical ports of the frame grabber to the logical ports on the camera operators in the way shown in the diagram above. (Looking at the diagram, there is a second possibility where the camera with master id 2 is connnected to the camera operator with master id 0. This might be the case if only one camera was connected when the discovery procedure was first run, and the second camera was added at a later point.)
Still, the same camera property, CAM_PROP_APPLETOPERATORINDEX
, is enough to know the correct DMA channel.
Registering a Callback Function for Discovery Events#
struct SgcEventInfo {
uint16_t eventID;
union SgcEventData data;
}
typedef int (* Sgc_BoardEventCallbackFunc_t)(
SgcBoardHandle * board,
const struct SgcEventInfo * info,
void * data);
int Sgc_registerBoardEventCallback(
SgcBoardHandle * board,
Sgc_BoardEventCallbackFunc_t func,
void * data);
int Sgc_scanPortsEx(
SgcBoardHandle * board,
unsigned int portMask,
int timeout,
int speed,
unsigned int flags);
Info
The function Sgc_registerBoardEventCallback()
was added in version 5.9 of the Framegrabber API.
Using Sgc_registerBoardEventCallback()
, a function of type SgcEventCallback_t
can be registered to be called back when an event is generated by the library. The function will be registered for a given board handle and will be passed three parameters when called. The first parameter to the callback function is the board handle. The second parameter is a pointer to a struct SgcEventInfo
which is used to store information about the event. The last parameter is a pointer which was supplied with the call to Sgc_registerBoardEventCallback()
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 board handle.
The discovery information about the camera is only updated when the function Sgc_scanPorts()
or Sgc_scanPortsEx()
is called. To only update the information, Sgc_scanPortsEx()
can be called from the callback function, using SGC_SP_FLAG_UPDATE_DISCOVERY_INFOS_ONLY
in the parameter flags
.
After the callback function is no longer needed, it can be unregistered by calling Sgc_registerBoardEventCallback()
, passing nullptr
to the parameters func
and data
.
The struct SgcEventInfo
instance contains the identification of the event in the member eventID
and additional data in the union member data
. The following event identifiers are used for events generated by the library:
Event | Description |
---|---|
SGC_EVENT_DISCOVERY_STATE_CAMERA_ADDED | a new camera was discovered |
SGC_EVENT_DISCOVERY_STATE_CAMERA_LOST | the connection to a camera was lost |
SGC_EVENT_DISCOVERY_STATE_CAMERA_PROPERTY_CHANGE | a property of the camera connection was changed |
The following example shows how to register a callback function using a simple structure which could hold information useful for the application:
struct DiscoveryUserCallbackData
{
// ...
};
int DiscoveryUserCallback(SgcBoardHandle * board,
const struct SgcEventInfo * info, void * data)
{
auto context = reinterpret_cast<DiscoveryUserCallbackData *>(data);
// update discovery infos
Sgc_scanPortsEx(board, 0xf, 0, LINK_SPEED_NONE,
SGC_SP_FLAG_UPDATE_DISCOVERY_INFOS_ONLY);
// process event ...
return 0;
}
void SetupDiscoveryUserCallback(SgcBoardHandle * board,
DiscoveryUserCallbackData * context)
{
// register callback function
int result =
Sgc_registerBoardEventCallback(board, &DiscoveryUserCallback, context);
if (result != SGC_OK) {
throw std::runtime_error("Failed to register callback function");
}
}
The allocation and management of the context structure isn't 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.
The Discovery State Changed Information#
struct SgcEventData_DiscoveryStateChanged {
uint32_t id;
uint32_t properties_int_count;
char ** properties_int_key;
uint32_t * properties_int_value;
uint32_t properties_string_count;
char ** properties_string_key;
char ** properties_string_value;
};
Info
The discovery state changed event was added in version 5.9 of the Framegrabber API.
For board events, struct SgcEventData_DiscoveryStateChanged
is used to provide information about the camera connection affected in the event. The structure contains the camera master id in the member id
and two key-value maps, one for integer values and one for string values. The keys are the same as for the properties available through Sgc_getCameraPropertyWithType()
, but depending on the event and the camera interface technology, not all properties will be present in the event info. The application can expect the relevant properties with respect to the change for which the event was generated to be present in the maps. Beyond this the application should not make assumptions on the presence of properties or the order of the properties within the maps.
All data in the structure is only valid in the event callback function. If information should be retained, the relevant parts must be copied.
The following example shows how to extract the link speed and number of links in an SGC_EVENT_DISCOVERY_STATE_CAMERA_PROPERTY_CHANGE
event:
int DiscoveryUserCallback(SgcBoardHandle * board,
const struct SgcEventInfo * info, void * data)
{
auto context = reinterpret_cast<DiscoveryUserCallbackData *>(data);
// update discovery infos
Sgc_scanPortsEx(board, 0xf, 0, LINK_SPEED_NONE,
SGC_SP_FLAG_UPDATE_DISCOVERY_INFOS_ONLY);
// process event ...
if (info->eventID == SGC_EVENT_DISCOVERY_STATE_CAMERA_PROPERTY_CHANGE) {
int newNumOfLinks = 0;
int newLinkSpeed = LINK_SPEED_NONE;
for (int i = 0; i < info->data.discovery_state.properties_int_count; ++i) {
if (strcmp(info->data.discovery_state.properties_int_keys[i],
CAM_PROP_NROFLINKS) == 0) {
newNumOfLinks = info->data.discovery_state.properties_int_values[i];
}
if (strcmp(info->data.discovery_state.properties_int_keys[i],
CAM_PROP_LINKSPEED) == 0) {
newLinkSpeed = info->data.discovery_state.properties_int_values[i];
}
}
// ...
}
return 0;
}
Using the GenICam Reference Implementation#
Using the GenICam reference implementation is an alternative to using the GenApi wrapper provided by the Framegrabber API as documented in chapter Working with Camera Features. If the application uses the GenICam reference implementation, the function Sgc_connectCamera()
or Sgc_connectCameraWithExternalXml()
must not be called. Instead, the application should include the GenApi header file GenICam.h
, link against the GenICam libraries GCBase
and GenApi
and initialize the corresponding GenApi context.
#include <GenICam.h>
To use the GenICam reference implementation which comes with the Framegrabber SDK installation, the following steps should be taken:
- If the application uses the 64-bit Framegrabber SDK on Windows:
- Add
%BASLER_FG_SDK_DIR%\genicam\bin\Linux64_x64
to the system environment variablePATH
- Add
%BASLER_FG_SDK_DIR%\genicam\library\CPP\include
to the project include directories - Add
%BASLER_FG_SDK_DIR%\genicam\library\CPP\lib\Win64_x64
to the project library directories - Add the libraries
GCBase_MD_VC141_v3_1_Basler_pylon.lib
andGenApi_MD_VC141_v3_1_Basler_pylon.lib
to your project link libraries - If the application uses the 32-bit Framegrabber SDK on Windows, replace
Win64_x64
withWin32_i86
in the steps above - If the application uses the 64-bit Framegrabber SDK on Linux:
- Add
$BASLER_FG_SDK_DIR\genicam\bin\Win64_x64
to the system environment variablePATH
and the project library directories - Add
$BASLER_FG_SDK_DIR\genicam\library\CPP\include
to the project include directories - Add the libraries
libGCBase_gcc48_v3_2.so
andlibGenApi_gcc48_v3_2.so
to your project link libraries - Add
#define GENICAM_NO_AUTO_IMPLIB
before#include <GenICam.h>
If you're using CMake to generate the project files, the package SisoGenicam will also look for the GenICam libraries which come with the Framegrabber SDK installation and include them in the variable ${SisoGenicam_LIBRARIES}
. The GenICam include path is stored in the variable ${GENICAM_INCLUDE_DIR}
which should be added to INCLUDE_DIRECTORIES
.
Creating an IPort Wrapper#
int Sgc_memoryReadFromCamera(
SgcCameraHandle * camera,
void * buffer,
uint64_t address,
size_t length);
int Sgc_memoryWriteToCamera(
SgcCameraHandle * camera,
const void * buffer,
uint64_t address,
size_t length);
To use the GenICam reference implementation a GENAPI_NAMESPACE::IPort
wrapper is required which translates access to the camera registers between the Framegrabber API and GenApi. The functions Sgc_memoryReadFromCamera()
and Sgc_memoryWriteToCamera()
can be used to create a simple and straight forward wrapper class:
class DevicePort: virtual public GENAPI_NAMESPACE::IPort
{
SgcCameraHandle * camera;
public:
DevicePort(SgcCameraHandle * camera)
: camera(camera)
{}
GENAPI_NAMESPACE::EAccessMode GetAccessMode() const override
{
return GENAPI_NAMESPACE::RW;
}
void Read(void * buffer, int64_t address, int64_t length) override
{
int result = Sgc_memoryReadFromCamera(camera, buffer, address, length);
if (result != SGC_OK) {
// handle error ...
}
}
void Write(const void * buffer, int64_t address, int64_t length) override
{
int result = Sgc_memoryWriteToCamera(camera, buffer, address, length);
if (result != SGC_OK) {
// handle error ...
}
}
};
Initializing and Connecting the Node Map#
int Sgc_loadCameraXml(
SgcCameraHandle * camera);
The final steps in connecting the GenICam reference implementation to the Framegrabber API are: getting the GenICam XML document for the camera, creating a node map and connecting it to the XML.
To download and extract the XML document from the camera, the functions Sgc_loadCameraXml()
and Sgc_getCameraPropertyWithType()
can be used. First, the function Sgc_loadCameraXml()
should be called to download the XML document from the camera. Then the function Sgc_getCameraPropertyWithType()
can be called passing CAM_PROP_XML_DATA
to the parameter property
and nullptr
to the parameter value
to request the size of the buffer required to extract the XML document. After allocating a buffer of the required size, Sgc_getCameraPropertyWithType()
can be called again, passing the pointer to the buffer in the parameter value
to extract the XML document.
The following example shows how to download and extract the XML document from the camera, initialize and connect the node map and set the width and height of the image to be transferred: (The example requires the C++ header file memory
for the std::unique_ptr
type and stdexcept
for the std::runtime_error
and std::exception
types.)
try {
// download camera XML
int result = Sgc_loadCameraXml(camera);
if (result != SGC_OK)
throw std::runtime_error("Failed to download camera XML");
// get size of XML
unsigned int size = 0;
unsigned int propertyType = SGC_PROPERTY_TYPE_STRING;
result =
Sgc_getCameraPropertyWithType(camera, CAM_PROP_XML_DATA,
nullptr, &propertyType, &size);
if (result != SGC_OK || size == 0)
throw std::runtime_error("Failed to determine size of camera XML");
// create buffer and extract camera XML
std::unique_ptr<char[]> xml(new char[size]);
result =
Sgc_getCameraPropertyWithType(camera, CAM_PROP_XML_DATA,
xml.get(), &propertyType, &size);
if (result != SGC_OK)
throw std::runtime_error("Failed to extract camera XML");
// create device port and node map
DevicePort port(camera);
GENAPI_NAMESPACE::CNodeMapRef nodeMap;
// initialize and connect node map
nodeMap._LoadXMLFromString(xml.get());
if (!nodeMap._Connect(&port, "Device"))
throw std::runtime_error("Failed to connect node map to device port");
// use node map to access camera features ...
// set width
GENAPI_NAMESPACE::CIntegerPtr width = nodeMap._GetNode("Width");
if (width != nullptr) {
width->SetValue(1920);
}
// set height
GENAPI_NAMESPACE::CIntegerPtr height = nodeMap._GetNode("Height");
if (height != nullptr) {
height->SetValue(1080);
}
} catch (GENICAM_NAMESPACE::GenericException & x) {
// handle GenICam exceptions ...
} catch (std::exception & x) {
// handle C++ standard exceptions ...
}
Changing the Camera Connection#
int Sgc_setCameraPropertyWithType(
SgcCameraHandle * camera,
const char * property,
const void * value,
unsigned int type,
void * additionalValue);
To change the link speed or the number of links used by the camera, the function Sgc_setCameraPropertyWithType()
can be called. It works in a similar way as the function Sgc_getCameraPropertyWithType()
described in chapter Camera Information.
There are two properties which can be changed:
Property | Description | Type |
---|---|---|
CAM_PROP_LINKSPEED | Current link speed (in Mbit/s) | uint32_t |
CAM_PROP_NROFLINKS | Number of physical links used for image transfer | uint32_t |
Support for Camera Discovery from Multiple Processes#
int Sgc_initBoardEx(
Fg_Struct * fg,
unsigned int flags,
SgcBoardHandle ** board,
unsigned int portMask,
unsigned int mode);
int Sgc_scanPortsEx(
SgcBoardHandle * board,
unsigned int portMask,
int timeout,
int speed,
unsigned int flags);
Info
Support for camera discovery from multiple processes was added in version 5.9 of the Framegrabber API.
If the application uses more than one process to access cameras on a single frame grabber, only one process per physical port on the frame grabber is supported by the Framegrabber API. While a single process can access more than one physical port, any physical port must not be accessed through the Framegrabber API from multiple processes.
In a multi-process application, the access to physical ports can be limited by replacing calls to Sgc_initBoard()
and Sgc_scanPorts()
with Sgc_initBoardEx()
and Sgc_scanPortsEx()
and using the port mask. In addition, SGC_SP_FLAG_SKIP_AUTO_PORTADJUSTMENT
should be used in the call to Sgc_scanPortsEx()
in the parameter flags
to suppress remaping physical ports.
The port mask uses one bit per physical port, enabling access to the port when the bit is 1 and disbaling access when it is 0. Bit 0 corresponds to physical port 0 and so on. In the default port mask 0xf
the bits for all four ports are 1, so access to all four ports is enabled.
The scenario easiest to understand is a setup with one frame grabber and four cameras, each connected to a single physical port. The first process can use port mask 0x1
, the second 0x2
, the third 0x4
and the fourth 0x8
. Each process will be responsible for initialization and discovery on one physical port.
It gets a bit more complicated when we consider cameras connected with more than one link, because the cameras must always be connected to the correct physical ports. If for example the first process uses port mask 0x3
and the second uses port mask 0xc
, the cables for the cameras must under all circumstances be connected to ports 0 and 1 for the first camera, and 2 and 3 for the second. If at any point the cameras are connected in a different way, the discovery procedure will fail.
-
Both a camera and a camera operator in the applet can be identified by their master id. The lowest valid master id is 1. The camera master id is a value generated from the frame grabber board index and the physical port number the camera master port is connected to. The camera operator master id is a value generated from the logical port to which the operator master port is connected to. ↩↩