Impact Acquire SDK C++
ContinuousCaptureGenDC.cpp

The ContinuousCaptureGenDC program is a simple example to illustrate the handling of unprocessed GenICam™ GenDC buffers. It is based on the ContinuousCapture.cpp example concerning the acquisition implementation.

Since
3.4.0
Note
  • On Linux this example can only be built when using CMake with C++11 compliant compiler (e.g. gcc 5.0 or greater).
  • On Windows when working with Visual Studio® at least version 2015 is needed since version 3.5.0 of Impact Acquire.
  • The device which should be used has to support GenDC data transfer.
ContinuousCaptureGenDC example:
  1. Opens a Balluff device.
  2. Enables GenDC streaming of the device.
  3. Disables GenDC parser of Impact Acquire.
  4. Snaps images continuously.
  5. Parses the received data.
  6. Displays the snapped images from the GenDC container (on Windows platforms only).
Console Output
No command-line parameters specified.
Available command-line parameters:
  'serial' or 's' to specify the serial number of the device to use (if not specified the user will be asked to select a device)

USAGE EXAMPLE:

[0]: XXXXXXXX (BVS CA-XY, GigEVisionDevice, interface layout: GenICam, acquisition start/stop behaviour: User)

Please enter the number in front of the listed device followed by [ENTER] to open it: 0
Initialising the device. This might take some time...
Press [ENTER] to stop the acquisition thread

A GenDC Container has been captured! Start parsing it at '0000024631FE5800', but don't read beyond '0000024632030C00' because the size is 308224 bytes!
The container contains: 1 components and 1 parts. Only the first part will be displayed.
Part 1 contains an image of: 640x480 pixels @ 1 bytes per pixel
How it works

The acquisition is similar to the typical continuous capture. The only major difference is, that the GenDC transfer of the device is enabled and the internal GenDC parser is disabled. Afterwards the image acquisition is started and the received GenDC buffers are used to create a GenDCContainer which is used as a convenience object for handling GenD buffers.

Details on how to parse a GenICam™ GenDC descriptor can be found in the GenDC specification

However the general stuff (selection of a device etc.) is similar to the SingleCapture.cpp source example so the more basic functionalities will be explained within this example.

First of all the user is prompted to select the device he wants to use for this sample:

DeviceManager devMgr;
Device* pDev = getDeviceFromUserInput( devMgr );

Then after the device has been initialized successfully and the required settings are applied using the specific classes mvIMPACT::acquire::SystemSettings and mvIMPACT::acquire::GenICam::TransportLayerControl:

GenICam::TransportLayerControl tlc( pDev );
if( tlc.genDCStreamingMode.isValid() )
{
if( tlc.genDCStreamingMode.isWriteable() )
{
tlc.genDCStreamingMode.writeS( "On" );
}
else
{
cout << "The device does not allow write access to the '" << tlc.genDCStreamingMode.name()
<< "' feature! Not sure how far we will get but we will try anyway." << endl;
}
}
else
{
cout << "The device does not support GenDC data transfer. To continue would be futile!" << endl
<< "Press [ENTER] to end the application" << endl;
cin.get();
return 1;
}
SystemSettings sys( pDev );
if( sys.genDCParserEnable.isValid() )
{
sys.genDCParserEnable.write( bFalse );
}
else
{
Info info( pDev );
cout << "This version (" << info.driverVersion.readS() << ") of the " << PRODUCT_NAME
<< " framework does not fully support GenDC parsing. An update would fix this. To continue would be futile!" << endl
<< "Press [ENTER] to end the application" << endl;
cin.get();
return 1;
}

Once the settings have been applied a continuous acquisition is started using the mvIMPACT::acquire::helper::RequestProvider class:

helper::RequestProvider requestProvider( pDev );
requestProvider.acquisitionStart( myThreadCallback, std::ref( threadParam ) );

The function myThreadCallback will be called for each mvIMPACT::acquire::Request object becoming ready. An application can define an arbitrary function with arbitrary parameters for this operation. The first parameter however always has to be std::shared_ptr<mvIMPACT::acquire::Request>.

In this example the thread callback looks like this:

//-----------------------------------------------------------------------------
void myThreadCallback( shared_ptr<Request> pRequest, ThreadParameter& threadParameter )
//-----------------------------------------------------------------------------
{
++threadParameter.requestsCaptured_;
// display some statistical information every 100th image
if( threadParameter.requestsCaptured_ % 100 == 0 )
{
const Statistics& s = threadParameter.statistics_;
cout << "Info from " << threadParameter.pDev_->serial.read()
<< ": " << s.framesPerSecond.name() << ": " << s.framesPerSecond.readS()
<< ", " << s.errorCount.name() << ": " << s.errorCount.readS()
<< ", " << s.captureTime_s.name() << ": " << s.captureTime_s.readS() << endl;
}
if( pRequest->isOK() )
{
if( pRequest->payloadType.read() == ptGenDC )
{
const char* pContainer = reinterpret_cast<const char*>( pRequest->imageData.read() );
// unfortunately platform independent 64-bit 'printf' formatting is a mess due to lacking standards while
// 'cout' style formatting is extremely hard to read when it comes to outputting hex data, pointers and
// such thus the code doing the console output might look a little messy in the eyes of an aesthete
// but it does the job!
ostringstream oss;
oss << pRequest->bufferSizeFilled.read();
printf( "A GenDC Container has been captured! Start parsing it at '%p', but don't read beyond '%p' because the full GenDC container size is reported as %s bytes!\n",
reinterpret_cast<const void*>( pContainer ),
reinterpret_cast<const void*>( pContainer + pRequest->bufferSizeFilled.read() ),
oss.str().c_str() );
GenDC::GenDCContainerHeaderBase* pHeader = reinterpret_cast<GenDC::GenDCContainerHeaderBase*>( pRequest->imageData.read() );
// All the 'leXY_to_cpu' functions are just here for completeness: GenDC containers by definition are using
// little-endian while this code in theory (even though this is not very likely) might run on a big-endian machine.
const uint64_t descriptorSize = static_cast<uint64_t>( threadParameter.le32_to_cpu( pHeader->DescriptorSize ) );
const uint64_t bufferSizeFilled = static_cast<uint64_t>( pRequest->bufferSizeFilled.read() );
if( descriptorSize <= bufferSizeFilled )
{
#ifdef USE_DISPLAY
bool boDisplayUsed = false;
#endif // #ifdef USE_DISPLAY
const uint32_t componentCount = threadParameter.le32_to_cpu( pHeader->ComponentCount );
const uint64_t* pComponentOffsets = reinterpret_cast<const uint64_t*>( pContainer + sizeof( GenDC::GenDCContainerHeaderBase ) );
printf( " signature: 0x%08x, Version: %u.%u.%u\n",
threadParameter.le32_to_cpu( static_cast<uint32_t>( pHeader->Signature ) ),
pHeader->Version.Major,
pHeader->Version.Minor,
pHeader->Version.SubMinor );
printf( " type: 0x%04x, flags: 0x%04x, size: %u\n",
threadParameter.le16_to_cpu( static_cast<uint16_t>( pHeader->HeaderType ) ),
threadParameter.le16_to_cpu( pHeader->Flags.Value ),
threadParameter.le32_to_cpu( pHeader->HeaderSize ) );
// and so on for 'pHeader'...
if( componentCount > 0 )
{
if( pHeader->Flags.Field.ComponentInvalid )
{
cout << " The container might contain at least one invalid component." << endl;
}
const bool boContainerIsPreliminary = checkAndDisplayIfPreliminary( pHeader );
for( uint32_t i = 0; i < componentCount; i++ )
{
const GenDC::GenDCComponentHeaderBase* pComponentHeader = reinterpret_cast<const GenDC::GenDCComponentHeaderBase*>( pContainer + threadParameter.le64_to_cpu( pComponentOffsets[i] ) );
const uint16_t partCount = threadParameter.le16_to_cpu( pComponentHeader->PartCount );
// A component is 'invalid' only if both flags are set
bool boComponentIsInvalid = pHeader->Flags.Field.ComponentInvalid && pComponentHeader->Flags.Field.Invalid;
printf( " [component %u] type: 0x%04x, flags: 0x%04x%s, size: %u\n", i,
threadParameter.le16_to_cpu( static_cast<uint16_t>( pComponentHeader->HeaderType ) ),
threadParameter.le16_to_cpu( pComponentHeader->Flags.Value ),
boComponentIsInvalid ? " INVALID" : "",
threadParameter.le32_to_cpu( pComponentHeader->HeaderSize ) );
printf( " [component %u] source id: 0x%04x, group id: 0x%04x, region id: 0x%04x\n", i,
threadParameter.le16_to_cpu( pComponentHeader->SourceId ),
threadParameter.le16_to_cpu( pComponentHeader->GroupId ),
threadParameter.le16_to_cpu( pComponentHeader->RegionId ) );
// and so on for this 'component'! You might want to cast 'pComponentHeader' to a more specific GenDCComponentHeader somewhere around here once you
// know what you are dealing with
// We should not parse components marked as invalid.
if( !boComponentIsInvalid )
{
const uint64_t* pPartOffsets = reinterpret_cast<const uint64_t*>( reinterpret_cast<const char*>( pComponentHeader ) + sizeof( GenDC::GenDCComponentHeaderBase ) );
for( uint32_t j = 0; j < partCount; j++ )
{
const GenDC::GenDCPartHeader* pPartHeader = reinterpret_cast<const GenDC::GenDCPartHeader*>( pContainer + threadParameter.le64_to_cpu( pPartOffsets[j] ) );
const uint16_t partType = threadParameter.le16_to_cpu( static_cast<uint16_t>( pPartHeader->HeaderType ) );
printf( " [component %u][part %u] type: 0x%04x, flags: 0x%04x, size: %u\n", i, j, partType,
threadParameter.le16_to_cpu( pPartHeader->Flags.Value ),
threadParameter.le32_to_cpu( pPartHeader->HeaderSize ) );
// If a container is preliminary it may be possible to parse more of it depending on the flags
// contained in 'VariableFields'. However, for simplicity, we won't pass any parts at all in preliminary containers.
if( !boContainerIsPreliminary )
{
if( partType == GDC_2D )
{
const GenDC::GenDCPartHeader2D* pPartHeader2D = reinterpret_cast<const GenDC::GenDCPartHeader2D*>( pPartHeader );
printf( " [component %u][part %u] format: 0x%08x, %ux%u", i, j,
threadParameter.le32_to_cpu( static_cast<uint32_t>( pPartHeader2D->Format ) ),
threadParameter.le32_to_cpu( pPartHeader2D->SizeX ),
threadParameter.le32_to_cpu( pPartHeader2D->SizeY ) );
#ifdef USE_DISPLAY
if( !boDisplayUsed )
{
TFormatFlags formatFlag = TFormatFlags::ffRaw;
int bitsPerPixel = 0;
const void* ppData[1];
ppData[0] = pContainer + threadParameter.le64_to_cpu( pPartHeader2D->DataOffset );
if( threadParameter.displayWindow_.GetImageDisplay().ConvertFormatFromPFNC32(
threadParameter.le32_to_cpu( static_cast<uint32_t>( pPartHeader2D->Format ) ),
&formatFlag, &bitsPerPixel ) )
{
threadParameter.displayWindow_.GetImageDisplay().SetImage( ppData, 1, formatFlag,
threadParameter.le32_to_cpu( pPartHeader2D->SizeX ),
threadParameter.le32_to_cpu( pPartHeader2D->SizeY ),
bitsPerPixel,
uint32_t( ( pPartHeader->DataSize - pPartHeader2D->PaddingY ) / pPartHeader2D->SizeY ) );
threadParameter.displayWindow_.GetImageDisplay().Update();
boDisplayUsed = true;
}
else
{
cout << " (unsupported by display)";
}
}
#endif // #ifdef USE_DISPLAY
cout << endl;
}
// and so on for this 'part'!
}
else
{
cout << " The container is marked as PRELIMINARY - skipping parse of all parts." << endl;
}
}
} // for all parts
else
{
cout << " Component " << i << " is marked as INVALID - skipping parse of all parts." << endl;
}
}
}
else
{
cout << " The container apparently includes no components whatsoever, so trying to parse it would be futile!" << endl;
}
}
else
{
cout << "ERROR: Received a GenDC container descriptor for block " << pRequest->infoFrameID.read()
<< ", that claims to be larger (" << descriptorSize << " bytes) than the total amount of data captured ("
<< bufferSizeFilled << ")." << endl;
}
}
else
{
cout << "Non GenDC data has been captured (which is kind of unexpected): Payload type: " << pRequest->payloadType.readS() << ", "
<< ", pixel format: " << pRequest->imagePixelFormat.readS() << ", "
<< pRequest->imageOffsetX.read() << "x" << pRequest->imageOffsetY.read()
<< "@" << pRequest->imageWidth.read() << "x" << pRequest->imageHeight.read() << endl;
}
}
else
{
cout << "Error: " << pRequest->requestResult.readS() << endl;
}
}

Once it has been checked if the Request object has been processed successfully by using the mvIMPACT::acquire::Request::isOK function the type of the request's delivered payload is checked:

if( pRequest->payloadType.read() == ptGenDC )
{
// all good! Start parsing GenDC buffer!
}
else
{
cout << "Non GenDC data has been captured (which is kind of unexpected): Payload type: " << pRequest->payloadType.readS() << ", "
<< ", pixel format: " << pRequest->imagePixelFormat.readS() << ", "
<< pRequest->imageOffsetX.read() << "x" << pRequest->imageOffsetY.read()
<< "@" << pRequest->imageWidth.read() << "x" << pRequest->imageHeight.read() << endl;
}

Afterwards the address of the payload is used to start accessing the GenICam™ GenDC constiner:

const char* pContainer = reinterpret_cast<const char*>( pRequest->imageData.read() );
cout << "A GenDC Container has been captured! Start parsing it at '" << pContainer << "', but don't read beyond '" << static_cast<const char*>( pContainer + pRequest->imageSize.read() ) << "' because the full GenDC container size is reported as " << pRequest->imageSize.read() << " bytes!" << endl;
GenDC::GenDCContainerHeaderBase* pHeader = reinterpret_cast<GenDC::GenDCContainerHeaderBase*>( pRequest->imageData.read() );
Note
The parsing of the GenDC data is shown by the example but every detail about what is inside such a container is beyond the scope of this application and can be found in the corresponding specifications: https://www.emva.org/standards-technology/genicam/genicam-downloads

The information extracted from the GenDC buffer can be accessed via data types as defined by the GenDC.h header file, which is part of the GenICam&trade GenDC standard like it can be seen in the above thread callback.

On Windows platforms the image from the first part carrying 2D image data will be displayed. For the correct handling of the pixel format, it is necessary to have some knowledge about the PFNC specific pixel formats. More information about the PFNC formats can be found in the PFNC specification and the reference header.

#ifdef USE_DISPLAY
if( !boDisplayUsed )
{
TFormatFlags formatFlag = TFormatFlags::ffRaw;
int bitsPerPixel = 0;
const void* ppData[1];
ppData[0] = pContainer + threadParameter.le64_to_cpu( pPartHeader2D->DataOffset );
if( threadParameter.displayWindow_.GetImageDisplay().ConvertFormatFromPFNC32(
threadParameter.le32_to_cpu( static_cast<uint32_t>( pPartHeader2D->Format ) ),
&formatFlag, &bitsPerPixel ) )
{
threadParameter.displayWindow_.GetImageDisplay().SetImage( ppData, 1, formatFlag,
threadParameter.le32_to_cpu( pPartHeader2D->SizeX ),
threadParameter.le32_to_cpu( pPartHeader2D->SizeY ),
bitsPerPixel,
uint32_t( ( pPartHeader->DataSize - pPartHeader2D->PaddingY ) / pPartHeader2D->SizeY ) );
threadParameter.displayWindow_.GetImageDisplay().Update();
boDisplayUsed = true;
}
else
{
cout << " (unsupported by display)";
}
}
#endif // #ifdef USE_DISPLAY

As the GenICam™ GenDC buffer delivers the pixel format of the image data as GenICam™ PFNC format, it is therefore necessary to convert it to the corresponding mvIMPACT::acquire::display::TFormatFlags format when the display functionality of this framework shall be used.

threadParameter.displayWindow_.GetImageDisplay().ConvertFormatFromPFNC32( threadParameter.le32_to_cpu( static_cast<uint32_t>( pPartHeader2D->Format ) ), &formatFlag, &bitsPerPixel );
See also
Source code
//
// @description: Example applications for Impact Acquire
// @copyright: Copyright (C) 2024 Balluff GmbH
// @authors: APIs and drivers development team at Balluff GmbH
// @initial date: 2024-09-11
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,i
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
#include <apps/Common/exampleHelper.h>
#include <functional>
#include <memory>
#include <mvIMPACT_CPP/mvIMPACT_acquire_helper.h>
#include <mvIMPACT_CPP/mvIMPACT_acquire_GenICam.h>
#ifdef _WIN32
# include <mvDisplay/Include/mvIMPACT_acquire_display.h>
# define USE_DISPLAY
#endif // #ifdef _WIN32
#include <Toolkits/GenICam_Standard/PFNC/PFNC.h>
#include <Toolkits/GenICam_Standard/GenDC/GenDC.h>
using namespace GenDC;
using namespace mvIMPACT::acquire;
using namespace std;
//=============================================================================
//================= Data type definitions =====================================
//=============================================================================
enum THostByteOrder : uint64_t
{
O64_LITTLE_ENDIAN = 0x0706050403020100ULL,
O64_BIG_ENDIAN = 0x0001020304050607ULL
};
//-----------------------------------------------------------------------------
/// \brief A small helper class to check the byte order of the host platform
class O64HostByteOrderEvaluator
//-----------------------------------------------------------------------------
{
union Helper
{
unsigned char bytes[8];
uint64_t value;
} h_;
public:
explicit O64HostByteOrderEvaluator()
{
for( unsigned char i = 0; i < 8; i++ )
{
h_.bytes[i] = i;
}
}
THostByteOrder get64BitHostByteOrder( void ) const
{
return static_cast<THostByteOrder>( h_.value );
}
};
//-----------------------------------------------------------------------------
struct ThreadParameter
//-----------------------------------------------------------------------------
{
Device* pDev_;
unsigned int requestsCaptured_;
Statistics statistics_;
O64HostByteOrderEvaluator O64HostByteOrderEvaluator_;
#ifdef USE_DISPLAY
ImageDisplayWindow displayWindow_;
#endif // #ifdef USE_DISPLAY
explicit ThreadParameter( Device* pDev ) : pDev_( pDev ), requestsCaptured_( 0 ), statistics_( pDev )
#ifdef USE_DISPLAY
// initialise display window
// IMPORTANT: It's NOT safe to create multiple display windows in multiple threads!!!
, displayWindow_( "mvIMPACT_acquire sample, Device " + pDev_->serial.read() )
#endif // #ifdef USE_DISPLAY
{}
ThreadParameter( const ThreadParameter& src ) = delete;
ThreadParameter& operator=( const ThreadParameter& rhs ) = delete;
//-----------------------------------------------------------------------------
/// \brief A template function to convert 16 bit values from little endian byte order
/// to the host platform's byte order
template<typename _Ty>
_Ty le16_to_cpu( _Ty value )
//-----------------------------------------------------------------------------
{
switch( O64HostByteOrderEvaluator_.get64BitHostByteOrder() )
{
case O64_LITTLE_ENDIAN:
break;
case O64_BIG_ENDIAN:
return ( ( value & 0xff00 ) >> 8 ) | \
( ( value & 0x00ff ) << 8 );
break;
}
return value;
}
//-----------------------------------------------------------------------------
/// \brief A template function to convert 32 bit values from little endian byte order
/// to the host platform's byte order
template<typename _Ty>
_Ty le32_to_cpu( _Ty value )
//-----------------------------------------------------------------------------
{
switch( O64HostByteOrderEvaluator_.get64BitHostByteOrder() )
{
case O64_LITTLE_ENDIAN:
break;
case O64_BIG_ENDIAN:
return ( ( value & 0xff000000 ) >> 24 ) | \
( ( value & 0x00ff0000 ) >> 8 ) | \
( ( value & 0x0000ff00 ) << 8 ) | \
( ( value & 0x000000ff ) << 24 );
break;
}
return value;
}
//-----------------------------------------------------------------------------
/// \brief A template function to convert 64 bit values from little endian byte order
/// to the host platform's byte order
template<typename _Ty>
_Ty le64_to_cpu( _Ty value )
//-----------------------------------------------------------------------------
{
switch( O64HostByteOrderEvaluator_.get64BitHostByteOrder() )
{
case O64_LITTLE_ENDIAN:
break;
case O64_BIG_ENDIAN:
return ( ( value & 0x00000000000000ff ) << 56 ) | \
( ( value & 0x000000000000ff00 ) << 40 ) | \
( ( value & 0x0000000000ff0000 ) << 24 ) | \
( ( value & 0x00000000ff000000 ) << 8 ) | \
( ( value & 0x000000ff00000000 ) >> 8 ) | \
( ( value & 0x0000ff0000000000 ) >> 24 ) | \
( ( value & 0x00ff000000000000 ) >> 40 ) | \
( ( value & 0xff00000000000000 ) >> 56 );
break;
}
return value;
}
};
//=============================================================================
//================= implementation ============================================
//=============================================================================
//-----------------------------------------------------------------------------
bool checkAndDisplayIfPreliminary( const GenDC::GenDCContainerHeaderBase* pHeader )
//-----------------------------------------------------------------------------
{
// variable fields must be 0 otherwise this is a preliminary container
const bool result = pHeader->VariableFields.Value != 0 ;
if( result )
{
// Preliminary containers can only be partially parsed. The data which may change is indicated by 'VariableFields'.
cout << " The container includes preliminary data - the following information may change:" << endl << " ";
if( pHeader->VariableFields.Field.DataSize )
{
cout << "DataSize ";
}
if( pHeader->VariableFields.Field.SizeX )
{
cout << "SizeX ";
}
if( pHeader->VariableFields.Field.SizeY )
{
cout << "SizeY ";
}
if( pHeader->VariableFields.Field.RegionOffset )
{
cout << "RegionOffset ";
}
if( pHeader->VariableFields.Field.Format )
{
cout << "Format ";
}
if( pHeader->VariableFields.Field.Timestamp )
{
cout << "Timestamp ";
}
if( pHeader->VariableFields.Field.ComponentCount )
{
cout << "ComponentCount ";
}
if( pHeader->VariableFields.Field.ComponentInvalid )
{
cout << "ComponentInvalid ";
}
if( pHeader->VariableFields.Field.Reserved )
{
cout << "Reserved ";
}
cout << endl;
}
return result;
}
//-----------------------------------------------------------------------------
void myThreadCallback( shared_ptr<Request> pRequest, ThreadParameter& threadParameter )
//-----------------------------------------------------------------------------
{
++threadParameter.requestsCaptured_;
// display some statistical information every 100th image
if( threadParameter.requestsCaptured_ % 100 == 0 )
{
const Statistics& s = threadParameter.statistics_;
cout << "Info from " << threadParameter.pDev_->serial.read()
<< ": " << s.framesPerSecond.name() << ": " << s.framesPerSecond.readS()
<< ", " << s.errorCount.name() << ": " << s.errorCount.readS()
<< ", " << s.captureTime_s.name() << ": " << s.captureTime_s.readS() << endl;
}
if( pRequest->isOK() )
{
if( pRequest->payloadType.read() == ptGenDC )
{
const char* pContainer = reinterpret_cast<const char*>( pRequest->imageData.read() );
// unfortunately platform independent 64-bit 'printf' formatting is a mess due to lacking standards while
// 'cout' style formatting is extremely hard to read when it comes to outputting hex data, pointers and
// such thus the code doing the console output might look a little messy in the eyes of an aesthete
// but it does the job!
ostringstream oss;
oss << pRequest->bufferSizeFilled.read();
printf( "A GenDC Container has been captured! Start parsing it at '%p', but don't read beyond '%p' because the full GenDC container size is reported as %s bytes!\n",
reinterpret_cast<const void*>( pContainer ),
reinterpret_cast<const void*>( pContainer + pRequest->bufferSizeFilled.read() ),
oss.str().c_str() );
GenDC::GenDCContainerHeaderBase* pHeader = reinterpret_cast<GenDC::GenDCContainerHeaderBase*>( pRequest->imageData.read() );
// All the 'leXY_to_cpu' functions are just here for completeness: GenDC containers by definition are using
// little-endian while this code in theory (even though this is not very likely) might run on a big-endian machine.
const uint64_t descriptorSize = static_cast<uint64_t>( threadParameter.le32_to_cpu( pHeader->DescriptorSize ) );
const uint64_t bufferSizeFilled = static_cast<uint64_t>( pRequest->bufferSizeFilled.read() );
if( descriptorSize <= bufferSizeFilled )
{
#ifdef USE_DISPLAY
bool boDisplayUsed = false;
#endif // #ifdef USE_DISPLAY
const uint32_t componentCount = threadParameter.le32_to_cpu( pHeader->ComponentCount );
const uint64_t* pComponentOffsets = reinterpret_cast<const uint64_t*>( pContainer + sizeof( GenDC::GenDCContainerHeaderBase ) );
printf( " signature: 0x%08x, Version: %u.%u.%u\n",
threadParameter.le32_to_cpu( static_cast<uint32_t>( pHeader->Signature ) ),
pHeader->Version.Major,
pHeader->Version.Minor,
pHeader->Version.SubMinor );
printf( " type: 0x%04x, flags: 0x%04x, size: %u\n",
threadParameter.le16_to_cpu( static_cast<uint16_t>( pHeader->HeaderType ) ),
threadParameter.le16_to_cpu( pHeader->Flags.Value ),
threadParameter.le32_to_cpu( pHeader->HeaderSize ) );
// and so on for 'pHeader'...
if( componentCount > 0 )
{
if( pHeader->Flags.Field.ComponentInvalid )
{
cout << " The container might contain at least one invalid component." << endl;
}
const bool boContainerIsPreliminary = checkAndDisplayIfPreliminary( pHeader );
for( uint32_t i = 0; i < componentCount; i++ )
{
const GenDC::GenDCComponentHeaderBase* pComponentHeader = reinterpret_cast<const GenDC::GenDCComponentHeaderBase*>( pContainer + threadParameter.le64_to_cpu( pComponentOffsets[i] ) );
const uint16_t partCount = threadParameter.le16_to_cpu( pComponentHeader->PartCount );
// A component is 'invalid' only if both flags are set
bool boComponentIsInvalid = pHeader->Flags.Field.ComponentInvalid && pComponentHeader->Flags.Field.Invalid;
printf( " [component %u] type: 0x%04x, flags: 0x%04x%s, size: %u\n", i,
threadParameter.le16_to_cpu( static_cast<uint16_t>( pComponentHeader->HeaderType ) ),
threadParameter.le16_to_cpu( pComponentHeader->Flags.Value ),
boComponentIsInvalid ? " INVALID" : "",
threadParameter.le32_to_cpu( pComponentHeader->HeaderSize ) );
printf( " [component %u] source id: 0x%04x, group id: 0x%04x, region id: 0x%04x\n", i,
threadParameter.le16_to_cpu( pComponentHeader->SourceId ),
threadParameter.le16_to_cpu( pComponentHeader->GroupId ),
threadParameter.le16_to_cpu( pComponentHeader->RegionId ) );
// and so on for this 'component'! You might want to cast 'pComponentHeader' to a more specific GenDCComponentHeader somewhere around here once you
// know what you are dealing with
// We should not parse components marked as invalid.
if( !boComponentIsInvalid )
{
const uint64_t* pPartOffsets = reinterpret_cast<const uint64_t*>( reinterpret_cast<const char*>( pComponentHeader ) + sizeof( GenDC::GenDCComponentHeaderBase ) );
for( uint32_t j = 0; j < partCount; j++ )
{
const GenDC::GenDCPartHeader* pPartHeader = reinterpret_cast<const GenDC::GenDCPartHeader*>( pContainer + threadParameter.le64_to_cpu( pPartOffsets[j] ) );
const uint16_t partType = threadParameter.le16_to_cpu( static_cast<uint16_t>( pPartHeader->HeaderType ) );
printf( " [component %u][part %u] type: 0x%04x, flags: 0x%04x, size: %u\n", i, j, partType,
threadParameter.le16_to_cpu( pPartHeader->Flags.Value ),
threadParameter.le32_to_cpu( pPartHeader->HeaderSize ) );
// If a container is preliminary it may be possible to parse more of it depending on the flags
// contained in 'VariableFields'. However, for simplicity, we won't pass any parts at all in preliminary containers.
if( !boContainerIsPreliminary )
{
if( partType == static_cast<uint16_t>( GDC_2D ) )
{
const GenDC::GenDCPartHeader2D* pPartHeader2D = reinterpret_cast<const GenDC::GenDCPartHeader2D*>( pPartHeader );
printf( " [component %u][part %u] format: 0x%08x, %ux%u", i, j,
threadParameter.le32_to_cpu( static_cast<uint32_t>( pPartHeader2D->Format ) ),
threadParameter.le32_to_cpu( pPartHeader2D->SizeX ),
threadParameter.le32_to_cpu( pPartHeader2D->SizeY ) );
#ifdef USE_DISPLAY
if( !boDisplayUsed )
{
TFormatFlags formatFlag = TFormatFlags::ffRaw;
int bitsPerPixel = 0;
const void* ppData[1];
ppData[0] = pContainer + threadParameter.le64_to_cpu( pPartHeader2D->DataOffset );
if( threadParameter.displayWindow_.GetImageDisplay().ConvertFormatFromPFNC32(
threadParameter.le32_to_cpu( static_cast<uint32_t>( pPartHeader2D->Format ) ),
&formatFlag, &bitsPerPixel ) )
{
threadParameter.displayWindow_.GetImageDisplay().SetImage( ppData, 1, formatFlag,
threadParameter.le32_to_cpu( pPartHeader2D->SizeX ),
threadParameter.le32_to_cpu( pPartHeader2D->SizeY ),
bitsPerPixel,
uint32_t( ( pPartHeader->DataSize - pPartHeader2D->PaddingY ) / pPartHeader2D->SizeY ) );
threadParameter.displayWindow_.GetImageDisplay().Update();
boDisplayUsed = true;
}
else
{
cout << " (unsupported by display)";
}
}
#endif // #ifdef USE_DISPLAY
cout << endl;
}
// and so on for this 'part'!
}
else
{
cout << " The container is marked as PRELIMINARY - skipping parse of all parts." << endl;
}
}
} // for all parts
else
{
cout << " Component " << i << " is marked as INVALID - skipping parse of all parts." << endl;
}
}
}
else
{
cout << " The container apparently includes no components whatsoever, so trying to parse it would be futile!" << endl;
}
}
else
{
cout << "ERROR: Received a GenDC container descriptor for block " << pRequest->infoFrameID.read()
<< ", that claims to be larger (" << descriptorSize << " bytes) than the total amount of data captured ("
<< bufferSizeFilled << ")." << endl;
}
}
else
{
cout << "Non GenDC data has been captured (which is kind of unexpected): Payload type: " << pRequest->payloadType.readS() << ", "
<< ", pixel format: " << pRequest->imagePixelFormat.readS() << ", "
<< pRequest->imageOffsetX.read() << "x" << pRequest->imageOffsetY.read()
<< "@" << pRequest->imageWidth.read() << "x" << pRequest->imageHeight.read() << endl;
}
}
else
{
cout << "Error: " << pRequest->requestResult.readS() << endl;
}
}
//-----------------------------------------------------------------------------
void displayCommandLineOptions( void )
//-----------------------------------------------------------------------------
{
cout << "Available command-line parameters:" << endl
<< " 'serial' or 's' to specify the serial number of the device to use (if not specified the user will be asked to select a device)" << endl
<< endl
<< "USAGE EXAMPLE:" << endl
<< " ContinuousCaptureGenDC s=BN*" << endl << endl;
}
//-----------------------------------------------------------------------------
// This function will allow to select devices that support the GenICam interface
// layout(these are devices, that claim to be compliant with the GenICam standard)
// and that are bound to drivers that support the user controlled start and stop
// of the internal acquisition engine. Other devices will not be listed for
// selection as the code of the example relies on these features in the code.
bool isDeviceSupportedBySample( const Device* const pDev )
//-----------------------------------------------------------------------------
{
if( !pDev->interfaceLayout.isValid() &&
{
return false;
}
vector<TDeviceInterfaceLayout> availableInterfaceLayouts;
pDev->interfaceLayout.getTranslationDictValues( availableInterfaceLayouts );
return find( availableInterfaceLayouts.begin(), availableInterfaceLayouts.end(), dilGenICam ) != availableInterfaceLayouts.end();
}
//-----------------------------------------------------------------------------
int main( int argc, char* argv[] )
//-----------------------------------------------------------------------------
{
DeviceManager devMgr;
Device* pDev = nullptr;
// scan command line
if( argc > 1 )
{
bool boInvalidCommandLineParameterDetected = false;
for( int i = 1; i < argc; i++ )
{
const string param( argv[i] );
cout << "Processing '" << param << "' now..." << endl;
const string::size_type keyEnd = param.find_first_of( "=" );
if( ( keyEnd == string::npos ) || ( keyEnd == param.length() - 1 ) )
{
cout << "Invalid command-line parameter: '" << param << "' (ignored)." << endl;
boInvalidCommandLineParameterDetected = true;
}
else
{
const string key = param.substr( 0, keyEnd );
const string value = param.substr( keyEnd + 1 );
if( ( key == "serial" ) || ( key == "s" ) )
{
pDev = devMgr.getDeviceBySerial( value );
}
else
{
cout << "Invalid command-line parameter: '" << param << "' (ignored)." << endl;
boInvalidCommandLineParameterDetected = true;
}
}
}
if( boInvalidCommandLineParameterDetected )
{
displayCommandLineOptions();
}
}
else
{
cout << "No command-line parameters specified." << endl;
displayCommandLineOptions();
}
if( pDev == nullptr )
{
pDev = getDeviceFromUserInput( devMgr, isDeviceSupportedBySample );
if( pDev == nullptr )
{
cout << "No device has been selected! Unable to continue! Press [ENTER] to end the application" << endl;
cin.get();
return 1;
}
}
cout << "Initialising the device. This might take some time..." << endl;
try
{
pDev->open();
}
catch( const ImpactAcquireException& e )
{
// this e.g. might happen if the same device is already opened in another process...
cout << "An error occurred while opening the device " << pDev->serial.read()
<< "(error code: " << e.getErrorCodeAsString() << ").";
return 1;
}
try
{
if( tlc.genDCStreamingMode.isValid() )
{
if( tlc.genDCStreamingMode.isWriteable() )
{
tlc.genDCStreamingMode.writeS( "On" );
}
else
{
cout << "The device does not allow write access to the '" << tlc.genDCStreamingMode.name()
<< "' feature! Not sure how far we will get but we will try anyway." << endl;
}
}
else
{
cout << "The device does not support GenDC data transfer. To continue would be futile!" << endl
<< "Press [ENTER] to end the application" << endl;
cin.get();
return 1;
}
SystemSettings sys( pDev );
if( sys.genDCParserEnable.isValid() )
{
sys.genDCParserEnable.write( bFalse );
}
else
{
Info info( pDev );
cout << "This version (" << info.driverVersion.readS() << ") of the " << PRODUCT_NAME
<< " framework does not fully support GenDC parsing. An update would fix this. To continue would be futile!" << endl
<< "Press [ENTER] to end the application" << endl;
cin.get();
return 1;
}
}
catch( const ImpactAcquireException& e )
{
// this e.g. might happen if the device does not support some of the utilized properties...
cout << "An error occurred while configuring the device for GenDC data transfer" << pDev->serial.read()
<< "(error code: " << e.getErrorCodeAsString() << ").";
return 1;
}
ThreadParameter threadParam( pDev );
helper::RequestProvider requestProvider( pDev );
cout << "Press [ENTER] to stop the acquisition thread" << endl;
requestProvider.acquisitionStart( myThreadCallback, std::ref( threadParam ) );
cin.get();
requestProvider.acquisitionStop();
return 0;
}
std::string name(void) const
Returns the name of the component referenced by this object.
Definition mvIMPACT_acquire.h:1206
bool isValid(void) const
Checks if the internal component referenced by this object is still valid.
Definition mvIMPACT_acquire.h:1721
Grants access to devices that can be operated by this software interface.
Definition mvIMPACT_acquire.h:7171
Device * getDeviceBySerial(const std::string &serial="", unsigned int devNr=0, char wildcard=' *') const
Tries to locate a device via the serial number.
Definition mvIMPACT_acquire.h:7524
This class and its functions represent an actual device detected by this interface in the current sys...
Definition mvIMPACT_acquire.h:6118
PropertyS serial
A string property (read-only) containing the serial number of this device.
Definition mvIMPACT_acquire.h:6551
void open(void)
Opens a device.
Definition mvIMPACT_acquire.h:6420
PropertyIDeviceInterfaceLayout interfaceLayout
An enumerated integer property which can be used to define which interface layout shall be used when ...
Definition mvIMPACT_acquire.h:6644
PropertyIAcquisitionStartStopBehaviour acquisitionStartStopBehaviour
An enumerated integer property defining the start/stop behaviour during acquisition of this driver in...
Definition mvIMPACT_acquire.h:6800
const EnumPropertyI & getTranslationDictValues(std::vector< ZYX > &sequence) const
This function queries a list of valid values for this property.
Definition mvIMPACT_acquire.h:4266
Category that contains the transport Layer control features.
Definition mvIMPACT_acquire_GenICam.h:13070
A base class for exceptions generated by Impact Acquire.
Definition mvIMPACT_acquire.h:256
std::string getErrorCodeAsString(void) const
Returns a string representation of the error associated with the exception.
Definition mvIMPACT_acquire.h:288
A base class to access various general information about the device and its driver.
Definition mvIMPACT_acquire.h:12116
std::string read(int index=0) const
Reads a value from a property.
Definition mvIMPACT_acquire.h:5323
std::string readS(int index=0, const std::string &format="") const
Reads data from this property as a string.
Definition mvIMPACT_acquire.h:3340
Contains basic statistical information.
Definition mvIMPACT_acquire.h:14509
PropertyF framesPerSecond
A float property (read-only) containing the current number of frames captured per second.
Definition mvIMPACT_acquire.h:14586
PropertyF captureTime_s
A float property (read-only) containing the overall time an image request spent in the device drivers...
Definition mvIMPACT_acquire.h:14560
PropertyI errorCount
An integer property (read-only) containing the overall count of image requests which returned with an...
Definition mvIMPACT_acquire.h:14568
A base class for accessing settings that control the overall behaviour of a device driver.
Definition mvIMPACT_acquire.h:14728
A class that can be used to display images in a window.
Definition mvIMPACT_acquire_display.h:606
A helper class that can be used to implement a simple continuous acquisition from a device.
Definition mvIMPACT_acquire_helper.h:432
TFormatFlags
Defines valid display pixel formats.
Definition mvDisplayDatatypes.h:137
This namespace contains classes and functions that can be used to display images.
This namespace contains classes and functions belonging to the image acquisition module of this SDK.
Definition mvCommonDataTypes.h:34