mvIMPACT Acquire SDK C++
Use Cases

GenICam To mvIMPACT Acquire Code Generator

Note
This section applies to the GenICam™ GenTL related mvIMPACT Acquire drivers only!

Using The Code Generator

As any GenICam™ compliant device for which there is a GenICam™ GenTL compliant capture driver in mvIMPACT Acquire can be used using the mvIMPACT Acquire interface and it can't be known which features are supported by a device until a device has been initialised and its GenICam™ XML file has been processed it is not possible to provide a complete C++ wrapper for every device statically.

Therefore an interface code generator has been embedded into mvGenTLConsumer library. This code generator can be used to create a convenient C++ interface file that allows access to every feature offered by a device.

Note
This generated interface can result in incompatibility issues, because the interface name will be constructed from the version and product information that comes with the GenICam XML file (see comment in code sample below). To avoid incompatibility, please use the common interface from the namespace mvIMPACT::acquire::GenICam whenever possible. It always contains all the features defined by the SFNC (Standard Feature Naming Convention(https://www.emva.org/standards-technology/genicam/genicam-downloads/))which was available at the time of the driver release as well as all MATRIX VISION specific features. Needed custom features for third party devices can easily be either added to existing class by deriving from them or by writing a small wrapper class for custom categories of features. In the latter case one of the existing class wrapping SFNC feature access can be used as a blueprint. The full source code is available in the corresponding header file. It is fairly easy to do!

To access features needed to generate a C++ wrapper interface a device needs to be initialized. Code can only be generated for the interface layout selected when the device was opened. If interfaces shall be created for more than a single interface layout the steps that will explain the creation of the wrapper files must be repeated for each interface layout.

Note
In a single application it rarely makes sense to work with the same device with multiple interface layouts!

Once the device has been opened the code generator can be accessed by navigating to "System Settings -> CodeGeneration".

Figure 1: wxPropView - Code Generation section


To generate code first of all an appropriate file name should be chosen. In order to prevent file name clashes the following hints should be kept in mind when thinking about a file name:

  • If several devices from different families or different vendors shall later be included into an application each device or device family will need its own header file thus either the files should be organized in different subfolders or must have unique names.
  • If a device shall be used using different interface layouts again different header files must be generated.

If only a single device family is involved but 2 interface layouts will be used later a suitable file name for one of these files might be "mvIMPACT_acquire_GenICam_Wrapper_DeviceSpecific.h".

For a more complex application involving different device families using the GenICam interface layout only something like this might make sense:

  • mvIMPACT_acquire_GenICam_Wrapper_MyDeviceA.h
  • mvIMPACT_acquire_GenICam_Wrapper_MyDeviceB.h
  • mvIMPACT_acquire_GenICam_Wrapper_MyDeviceC.h
  • ...

Once a file name has been selected the code generator can be invoked by executing the "int GenerateCode()" method:

Figure 2: wxPropView - GenerateCode() method


The result of the code generator run will be written into the "LastResult" property afterwards:

Figure 3: wxPropView - LastResult property


Using The Result Of The Code Generator In An Application

Each header file generated by the code generator will include "mvIMPACT_CPP/mvIMPACT_acquire.h" thus when an application is compiled with files that have been automatically generated these header files must have access to this file. This can easily achieved by appropriately settings up the build environment / Makefile.

To avoid problems of multiple includes the file will use an include guard build from the file name.

Within each header file the generated data types will reside in a sub namespace of "mvIMPACT::acquire" in order to avoid name clashes when working with several different created files in the same application. The namespace will automatically be generated from the ModelName tag and the file version tags in the devices GenICam XML file and the interface layout. For a device with a ModelName tag mvBlueIntelligentDevice and a file version of 1.1.0 something like this will be created:

namespace mvIMPACT {
  namespace acquire {
    namespace DeviceSpecific { // the name of the interface layout used during the process of code creation
      namespace MATRIX_VISION_mvBlueIntelligentDevice_1 { // this name will be constructed from the version and product
                                                          // information that comes with the GenICam XML file. As defined
                                                          // by the GenICam standard, different major versions of a devices
                                                          // XML file may not be compatible thus different interface files should be created here


// all code will reside in this inner namespace

      } // namespace MATRIX_VISION_mvBlueIntelligentDevice_1
    } // namespace DeviceSpecific
  } // namespace acquire
} // namespace mvIMPACT

In the application the generated header files can be used like any mvIMPACT Acquire header file:

#include <string>
// #include <mvIMPACT_CPP/mvIMPACT_acquire.h> // no longer needed as this is included by the generated header file anyway!
#include <mvIMPACT_CPP/mvIMPACT_acquire_GenICam_Wrapper_DeviceSpecific.h>

Now to access data types from the header files of course the namespaces must somehow be taken into account. When there is just a single interface that has been created automatically the easiest thing to do would probably an appropriate using statement:

using namespace mvIMPACT::acquire::DeviceSpecific::MATRIX_VISION_mvBlueIntelligentDevice_1;

If several files created from different devices shall be used and these devices define similar features in a slightly different way this however might result in name clashes and/or unexpected behaviour. In that case the namespaces should be specified explicitly when creating data type instances from the header file in the application:

//-----------------------------------------------------------------------------
void fn( Device* pDev )
//-----------------------------------------------------------------------------
{
  mvIMPACT::acquire::DeviceSpecific::MATRIX_VISION_mvBlueIntelligentDevice_1::DeviceControl dc( pDev );
  if( dc.timestampLatch.isValid() )
  {
    dc.timestampLatch.call();
  }
}

When working with a using statement the same code can be written like this as well:

//-----------------------------------------------------------------------------
void fn( Device* pDev )
//-----------------------------------------------------------------------------
{
  DeviceControl dc( pDev );
  if( dc.timestampLatch.isValid() )
  {
    dc.timestampLatch.call();
  }
}

Callbacks Triggered By GenICam Events

Note
This use case applies only to devices operating in GenICam interface layout. More details at chapter General and Which Interface Is Supported By Which Device?.

In some cases it is interesting to get notified about the occurrence of a certain event (e.g. a trigger signal or the end of an image being exposed) on the camera side as soon as possible. This can be achieved by using the events specified by the GeniCam standard in combination with callbacks.

Note
More details regarding the usage of callbacks are explained at the example Callback.cpp.

Depending on the firmware version the following events are currently supported:

  • General events:
    • EventExposureEnd
    • EventFrameEnd
  • Digital I/O related events:
    • EventLine4RisingEdge
    • EventLine4FallingEdge
    • EventLine4AnyEdge
    • EventLine5RisingEdge
    • EventLine5FallingEdge
    • EventLine5AnyEdge

The following example will illustrate how the events can be used to get notified about an image which has been exposed by the sensor, so the image has not been read out yet. This means the information about the finished image is available much earlier than the image itself. Depending on the resolution of the used device and the bandwidth of the used interface the saved time might be significant.

Note
This use case assumes that the image acquisition is already working and the used device supports the GenICam interface layout, similar to the ContinuousCapture.cpp sample.
How it works
  1. The device is opened by calling
    pThreadParameter->pDev->open();
  1. An instance of the EventControl class is created.
  2. The desired GenICam event of the device is enabled.
  3. A callback is created.
  4. The callback is registered to the property which should execute the callback once its value or features change.
  5. As soon as the image acquisition starts, the callback will be invoked.

Any application that wants to get notified when a certain feature in the mvIMPACT Acquire property tree did change needs to derive a class from mvIMPACT::acquire::ComponentCallback and override the mvIMPACT::acquire::ComponentCallback::execute() method:

//=============================================================================
//================= Event Callback implementation =============================
//=============================================================================
//-----------------------------------------------------------------------------
class EventCallback : public ComponentCallback
{
public:
explicit EventCallback( void* pUserData = 0 ) : ComponentCallback( pUserData ) {}
virtual void execute( Component& c, void* pUserData )
{
try
{
EventControl* ec = reinterpret_cast<EventControl*>( pUserData );
// Execute the followings if the component is a property.
if ( c.isProp() )
{
Property p( c );
// Show the property value in console when the property value changes due to the occurrence of the event.
cout << "Component " << c.name() << " has changed. Its current value: " << p.readS() << "us. FrameID is: " << ec->eventExposureEndFrameID.readS() << endl;
}
}
catch ( const ImpactAcquireException& e )
{
cout << "An error occurred while retrieving the callback value. Error code: " << e.getErrorCodeAsString() << ")." << endl;
}
}
};

In order to be able to do something useful each callback can carry a pointer to arbitrary user data that can be used to get access to any part of the application code within the callback context. This example attaches an instance of the class EventControl to get access to the events properties, when creating the callback handler later.

The event control class needs to be instantiated to get access to switch on the devices GenICam events. In this case the "ExposureEndEvent" is enabled and will be sent by the device once the exposure of an image has finished.

EventControl ec( pThreadParameter->pDev );
try
{
ec.eventSelector.writeS( "ExposureEnd" );
ec.eventNotification.writeS( "On" );
}
catch ( const ImpactAcquireException& e )
{
cout << "An error occurred while setting up event control to the device " << pThreadParameter->pDev->serial.read()
<< "(error code: " << e.getErrorCodeAsString() << ").";
}

Now the actual callback handler will be created and a property will be registered to it:

EventCallback eventCallback( &ec );
// register a callback to eventExposureEndTimestamp
eventCallback.registerComponent( ec.eventExposureEndTimestamp );

Once the callback is not needed anymore, it should be unregistered:

// clean up
eventCallback.unregisterComponent( ec.eventExposureEndTimestamp );

The code used for this use case can be found at: GenICamCallbackOnEvent.cpp

GigE Vision™

Unicast Device Discovery

This section in meant to explain that it is also possible to access GigE Vision™ devices residing in a different subnet using the mvIMPACT Acquire driver stack.

Attention
Please be aware that typically routers or network traffic that needs to gets routed through different subnets usually does not work as reliable as when only switches and cables are involved so the full bandwidth delivered by one or multiple GigE Vision™ devices might not be achievable but condition monitoring or slow streaming for analysis might be an interesting use case still. If possible routers supporting a higher link speed than the attached cameras should be used in order to ensure a reliable connection.

More details about unicast device discovery can be found in the mvIMPACT Acquire GUI manual in the "Detecting Devices Residing In A Different Subnet" chapter belonging to the mvIPConfigure tool as well as in the GigE Vision™ device manuals under "Use Cases" where the "Discovering Devices In Different Subnets" is of particular interest.

When using the mvIMPACT Acquire API it is important to note that everything that will be configured using the API will get stored permanently for every user working on a particular system. This e.g. allows to configure everything up front using mvIPConfigure if the network setup is static. After this has been done each application using the mvIMPACT Acquire driver stack will detect the remote devices just as every other device. In order to clear or change this the API or mvIPConfigure has to be used again.

The properties needed for this configuration are