Impact Acquire SDK C++
ContinuousCaptureAllDevices.cpp

The ContinuousCaptureAllDevices program is based on the ContinuousCapture.cpp example. It will try to open every device detected in the system and will display a live image with using the current settings stored for the individual device. Each live image will be displayed in a separate window.

Note
Please note that when there are a lot of devices connected to the system, the system might start to react slowly because of the massive amount of data it has to cope with. In that case the ContinuousCapture.cpp sample can be used to acquire live images from a particular device.
Program location
The source file ContinuousCaptureAllDevices.cpp can be found under:
%INSTALLDIR%\apps\ContinuousCaptureAllDevices\
Note
If you have installed the package without example applications, this file will not be available. On Windows® the sample application can be installed or removed from the target system at any time by simply restarting the installation package.
ContinuousCaptureAllDevices example:
  1. Opens a Balluff device.
  2. Snaps images from every detected device continuously (without display using Linux).
Console Output
[0]: BF000306 (mvBlueFOX-202C, Family: mvBlueFOX, interface layout: DeviceSpecific)

Please enter the number in front of the listed device followed by [ENTER] to open it: 0
Using device number 0.
Press [ENTER] to end the application
Initialising the device. This might take some time...
Info from BF000306: FramesPerSecond: 28.655660, ErrorCount: 0, CaptureTime_s: 0.104195
Info from BF000306: FramesPerSecond: 28.655636, ErrorCount: 0, CaptureTime_s: 0.104017
Info from BF000306: FramesPerSecond: 28.655659, ErrorCount: 0, CaptureTime_s: 0.104153
Info from BF000306: FramesPerSecond: 28.655636, ErrorCount: 0, CaptureTime_s: 0.104072
Info from BF000306: FramesPerSecond: 28.655660, ErrorCount: 0, CaptureTime_s: 0.104234
How it works

The continuous acquisition is similar to the single capture. The only major difference is, that this sample starts a separate thread that continuously requests images from the device.

However, the general stuff (selection of a device etc.) are similar to the SingleCapture.cpp source example. The major difference is that the user is not prompted to select a device as this application will use every recognized device anyway. Thus the main function looks slightly different while the actual acquisition thread remains unchanged (apart from some sync. Work that needs to be done for writing to the standard output).

//-----------------------------------------------------------------------------
int main( int argc, char* argv[] )
//-----------------------------------------------------------------------------
{
DeviceManager devMgr;
const unsigned int devCnt = devMgr.deviceCount();
if( devCnt == 0 )
{
cout << "No " << PRODUCT_NAME << " compliant device found! Unable to continue!" << endl;
return 1;
}
string productFilter( "*" );
bool boInvalidCommandLineParameterDetected = false;
// scan command line
if( argc > 1 )
{
for( int i = 1; i < argc; i++ )
{
const string param( argv[i] );
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 == "product" ) || ( key == "p" ) )
{
productFilter = value;
}
else
{
cout << "Invalid command line parameter: '" << param << "' (ignored)." << endl;
boInvalidCommandLineParameterDetected = true;
}
}
}
if( boInvalidCommandLineParameterDetected )
{
displayCommandLineOptions();
}
}
else
{
cout << "No command line parameters specified." << endl;
displayCommandLineOptions();
}
// store all device infos in a vector
// and start the execution of a 'live' thread for each device.
map<shared_ptr<ThreadParameter>, shared_ptr<thread>> threads;
for( unsigned int i = 0; i < devCnt; i++ )
{
if( match( devMgr[i]->product.read(), productFilter, '*' ) == 0 )
{
shared_ptr<ThreadParameter> pParameter = make_shared<ThreadParameter>( devMgr[i] );
#ifdef USE_DISPLAY
// IMPORTANT: It's NOT save to create multiple display windows in multiple threads!!!
// Therefore this must be done before starting the threads
pParameter->createDisplayWindow( "mvIMPACT_acquire sample, Device " + devMgr[i]->serial.read() );
#endif // #ifdef USE_DISPLAY
threads[pParameter] = make_shared<thread>( liveThread, pParameter );
lock_guard<mutex> lockedScope( s_mutex );
cout << devMgr[i]->family.read() << "(" << devMgr[i]->serial.read() << ")" << endl;
}
}
if( threads.empty() )
{
cout << "No device found that matches the product filter '" << productFilter << "'! Unable to continue!" << endl;
return 1;
}
// now all threads will start running...
{
lock_guard<mutex> lockedScope( s_mutex );
cout << "Press [ENTER] to end the acquisition( the initialisation of the devices might take some time )" << endl;
}
if( getchar() == EOF )
{
lock_guard<mutex> lockedScope( s_mutex );
cout << "'getchar()' did return EOF..." << endl;
}
// stop all threads again
{
lock_guard<mutex> lockedScope( s_mutex );
cout << "Terminating live threads..." << endl;
}
s_boRunning = false;
for( auto& it : threads )
{
it.second->join();
}
return 0;
}

Then after the device has been initialised successfully image requests will constantly be sent to the drivers request queue and the application waits for the results:

// Send all requests to the capture queue. There can be more than 1 queue for some devices, but for this sample
// we will work with the default capture queue. If a device supports more than one capture or result
// queue, this will be stated in the manual. If nothing is mentioned about it, the device supports one
// queue only. This loop will send all requests currently available to the driver. To modify the number of requests
// use the property mvIMPACT::acquire::SystemSettings::requestCount at runtime (note that some devices will
// only allow to modify this parameter while NOT streaming data!) or the property
// mvIMPACT::acquire::Device::defaultRequestCount BEFORE opening the device.
TDMR_ERROR result = DMR_NO_ERROR;
while( ( result = static_cast<TDMR_ERROR>( fi.imageRequestSingle() ) ) == DMR_NO_ERROR ) {};
if( result != DEV_NO_FREE_REQUEST_AVAILABLE )
{
lock_guard<mutex> lockedScope( s_mutex );
cout << "'FunctionInterface.imageRequestSingle' returned with an unexpected result: " << result
<< "(" << ImpactAcquireException::getErrorCodeAsString( result ) << ")" << endl;
}
manuallyStartAcquisitionIfNeeded( pDev, fi );
// run thread loop
const Request* pRequest = nullptr;
const unsigned int timeout_ms = {500};
int requestNr = {INVALID_ID};
// we always have to keep at least 2 images as the display module might want to repaint the image, thus we
// can't free it unless we have a assigned the display to a new buffer.
int lastRequestNr = {INVALID_ID};
while( s_boRunning )
{
// wait for results from the default capture queue
requestNr = fi.imageRequestWaitFor( timeout_ms );
if( fi.isRequestNrValid( requestNr ) )
{
pRequest = fi.getRequest( requestNr );
if( pRequest->isOK() )
{
// do something with the image
}
else
{
// some error: pRequest->requestResult.readS() will return a string representation
}
if( fi.isRequestNrValid( lastRequestNr ) )
{
// this image has been displayed thus the buffer is no longer needed...
fi.imageRequestUnlock( lastRequestNr );
}
lastRequestNr = requestNr;
// send a new image request into the capture queue
fi.imageRequestSingle();
}
else
{
// If the error code is -2119(DEV_WAIT_FOR_REQUEST_FAILED), the documentation will provide
// additional information under TDMR_ERROR in the interface reference
}
}
const int INVALID_ID
A constant to check for an invalid ID returned from the property handling module.
Definition mvPropHandlingDatatypes.h:62

With the request number returned by mvIMPACT::acquire::FunctionInterface::imageRequestWaitFor you can gain access the image buffer:

pRequest = fi.isRequestNrValid( requestNr ) ? fi.getRequest( requestNr ) : 0;

The image attached to the request can then be processed and/or displayed if the request does not report an error.

When the image is no longer needed you have to unlock the image buffer as otherwise the driver will refuse to use it again. This makes sure, that no image, that is still used by the user will be overwritten by the device:

pPreviousRequest->unlock();
Source code
//
// @description: Example applications for Impact Acquire
// @copyright: Copyright (C) 2005 - 2023 Balluff GmbH
// @authors: APIs and drivers development team at Balluff GmbH
// @initial date: 2005-03-15
//
// 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 <iostream>
#include <map>
#include <memory>
#include <mutex>
#include <thread>
#include <apps/Common/exampleHelper.h>
#include <mvIMPACT_CPP/mvIMPACT_acquire.h>
#ifdef _WIN32
# define USE_DISPLAY
# include <mvDisplay/Include/mvIMPACT_acquire_display.h>
#endif // #ifdef _WIN32
using namespace std;
using namespace mvIMPACT::acquire;
static mutex s_mutex;
static bool s_boTerminated = false;
//-----------------------------------------------------------------------------
class ThreadParameter
//-----------------------------------------------------------------------------
{
Device* pDev_;
#ifdef USE_DISPLAY
unique_ptr<ImageDisplayWindow> pDisplayWindow_;
#endif // #ifdef USE_DISPLAY
public:
explicit ThreadParameter( Device* pDev ) : pDev_( pDev ) {}
ThreadParameter( const ThreadParameter& src ) = delete;
Device* device( void ) const
{
return pDev_;
}
#ifdef USE_DISPLAY
void createDisplayWindow( const string& windowTitle )
{
pDisplayWindow_ = unique_ptr<ImageDisplayWindow>( new ImageDisplayWindow( windowTitle ) );
}
ImageDisplayWindow& displayWindow( void )
{
return *pDisplayWindow_;
}
#endif // #ifdef USE_DISPLAY
};
//-----------------------------------------------------------------------------
void displayCommandLineOptions( void )
//-----------------------------------------------------------------------------
{
cout << "Available parameters:" << endl
<< " 'product' or 'p' to specify a certain product type. All other products will be ignored then" << endl
<< " a '*' serves as a wildcard." << endl
<< endl
<< "USAGE EXAMPLE:" << endl
<< " ContinuousCaptureAllDevices p=mvBlue* " << endl << endl;
}
//-----------------------------------------------------------------------------
void writeToStdout( const string& msg )
//-----------------------------------------------------------------------------
{
lock_guard<mutex> lockedScope( s_mutex );
cout << msg << endl;
}
//-----------------------------------------------------------------------------
void liveThread( shared_ptr<ThreadParameter> parameter )
//-----------------------------------------------------------------------------
{
Device* pDev = parameter->device();
writeToStdout( "Trying to open " + pDev->serial.read() );
try
{
// if this device offers the 'GenICam' interface switch it on, as this will
// allow are better control over GenICam compliant devices
conditionalSetProperty( pDev->interfaceLayout, dilGenICam );
// if this device offers a user defined acquisition start/stop behaviour
// enable it as this allows finer control about the streaming behaviour
conditionalSetProperty( pDev->acquisitionStartStopBehaviour, assbUser );
pDev->open();
}
catch( const ImpactAcquireException& e )
{
lock_guard<mutex> lockedScope( s_mutex );
// 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.getErrorCode() << "(" << e.getErrorCodeAsString() << ")). Terminating thread." << endl
<< "Press [ENTER] to end the application..."
<< endl;
return;
}
writeToStdout( "Opened " + pDev->serial.read() );
// establish access to the statistic properties
Statistics statistics( pDev );
// create an interface to the device found
FunctionInterface fi( pDev );
// Send all requests to the capture queue. There can be more than 1 queue for some devices, but for this sample
// we will work with the default capture queue. If a device supports more than one capture or result
// queue, this will be stated in the manual. If nothing is mentioned about it, the device supports one
// queue only. This loop will send all requests currently available to the driver. To modify the number of requests
// use the property mvIMPACT::acquire::SystemSettings::requestCount at runtime (note that some devices will
// only allow to modify this parameter while NOT streaming data!) or the property
// mvIMPACT::acquire::Device::defaultRequestCount BEFORE opening the device.
while( ( result = static_cast<TDMR_ERROR>( fi.imageRequestSingle() ) ) == DMR_NO_ERROR ) {};
if( result != DEV_NO_FREE_REQUEST_AVAILABLE )
{
lock_guard<mutex> lockedScope( s_mutex );
cout << "'FunctionInterface.imageRequestSingle' returned with an unexpected result: " << result
<< "(" << ImpactAcquireException::getErrorCodeAsString( result ) << ")" << endl;
}
manuallyStartAcquisitionIfNeeded( pDev, fi );
// run thread loop
const Request* pRequest = nullptr;
const unsigned int timeout_ms = {500};
int requestNr = INVALID_ID;
// we always have to keep at least 2 images as the display module might want to repaint the image, thus we
// can't free it unless we have a assigned the display to a new buffer.
int lastRequestNr = {INVALID_ID};
unsigned int cnt = {0};
while( !s_boTerminated )
{
// wait for results from the default capture queue
requestNr = fi.imageRequestWaitFor( timeout_ms );
if( fi.isRequestNrValid( requestNr ) )
{
pRequest = fi.getRequest( requestNr );
if( pRequest->isOK() )
{
++cnt;
// here we can display some statistical information every 100th image
if( cnt % 100 == 0 )
{
lock_guard<mutex> lockedScope( s_mutex );
cout << "Info from " << pDev->serial.read()
<< ": " << statistics.framesPerSecond.name() << ": " << statistics.framesPerSecond.readS()
<< ": " << statistics.bandwidthConsumed.name() << ": " << statistics.bandwidthConsumed.readS()
<< ", " << statistics.errorCount.name() << ": " << statistics.errorCount.readS()
<< ", " << statistics.captureTime_s.name() << ": " << statistics.captureTime_s.readS() << endl;
}
#ifdef USE_DISPLAY
ImageDisplay& display = parameter->displayWindow().GetImageDisplay();
display.SetImage( pRequest );
display.Update();
#endif // #ifdef USE_DISPLAY
}
else
{
writeToStdout( "Error: " + pRequest->requestResult.readS() );
}
if( fi.isRequestNrValid( lastRequestNr ) )
{
// this image has been displayed thus the buffer is no longer needed...
fi.imageRequestUnlock( lastRequestNr );
}
lastRequestNr = requestNr;
// send a new image request into the capture queue
fi.imageRequestSingle();
}
//else
//{
// Please note that slow systems or interface technologies in combination with high resolution sensors
// might need more time to transmit an image than the timeout value which has been passed to imageRequestWaitFor().
// If this is the case simply wait multiple times OR increase the timeout(not recommended as usually not necessary
// and potentially makes the capture thread less responsive) and rebuild this application.
// Once the device is configured for triggered image acquisition and the timeout elapsed before
// the device has been triggered this might happen as well.
// The return code would be -2119(DEV_WAIT_FOR_REQUEST_FAILED) in that case, the documentation will provide
// additional information under TDMR_ERROR in the interface reference.
// If waiting with an infinite timeout(-1) it will be necessary to call 'imageRequestReset' from another thread
// to force 'imageRequestWaitFor' to return when no data is coming from the device/can be captured.
// cout << "imageRequestWaitFor failed (" << requestNr << ", " << ImpactAcquireException::getErrorCodeAsString( requestNr ) << ")"
// << ", timeout value too small?" << endl;
//}
}
manuallyStopAcquisitionIfNeeded( pDev, fi );
#ifdef USE_DISPLAY
// stop the display from showing freed memory
parameter->displayWindow().GetImageDisplay().RemoveImage();
#endif // #ifdef USE_DISPLAY
// In this sample all the next lines are redundant as the device driver will be
// closed now, but in a real world application a thread like this might be started
// several times an then it becomes crucial to clean up correctly.
// free the last potentially locked request
if( fi.isRequestNrValid( requestNr ) )
{
fi.imageRequestUnlock( requestNr );
}
// clear all queues
fi.imageRequestReset( 0, 0 );
}
//-----------------------------------------------------------------------------
int main( int argc, char* argv[] )
//-----------------------------------------------------------------------------
{
DeviceManager devMgr;
const unsigned int devCnt = devMgr.deviceCount();
if( devCnt == 0 )
{
cout << "No " << PRODUCT_NAME << " compliant device found! Unable to continue!" << endl;
return 1;
}
string productFilter( "*" );
// scan command line
if( argc > 1 )
{
bool boInvalidCommandLineParameterDetected = false;
for( int i = 1; i < argc; i++ )
{
const string param( argv[i] );
const auto 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 ) );
if( ( key == "product" ) || ( key == "p" ) )
{
productFilter = param.substr( keyEnd + 1 );
}
else
{
cout << "Invalid command line parameter: '" << param << "' (ignored)." << endl;
boInvalidCommandLineParameterDetected = true;
}
}
}
if( boInvalidCommandLineParameterDetected )
{
displayCommandLineOptions();
}
}
else
{
cout << "No command line parameters specified." << endl;
displayCommandLineOptions();
}
// store all device infos in a vector
// and start the execution of a 'live' thread for each device.
map<shared_ptr<ThreadParameter>, shared_ptr<thread>> threads;
for( unsigned int i = 0; i < devCnt; i++ )
{
if( match( devMgr[i]->product.read(), productFilter, '*' ) == 0 )
{
shared_ptr<ThreadParameter> pParameter = make_shared<ThreadParameter>( devMgr[i] );
#ifdef USE_DISPLAY
// IMPORTANT: It's NOT safe to create multiple display windows in multiple threads!!!
// Therefore this must be done before starting the threads
pParameter->createDisplayWindow( "mvIMPACT_acquire sample, Device " + devMgr[i]->serial.read() );
#endif // #ifdef USE_DISPLAY
threads[pParameter] = make_shared<thread>( liveThread, pParameter );
writeToStdout( devMgr[i]->family.read() + "(" + devMgr[i]->serial.read() + ")" );
}
}
if( threads.empty() )
{
cout << "No device found that matches the product filter '" << productFilter << "'! Unable to continue!" << endl;
return 1;
}
// now all threads will start running...
writeToStdout( "Press [ENTER] to end the acquisition( the initialisation of the devices might take some time )" );
if( getchar() == EOF )
{
writeToStdout( "'getchar()' did return EOF..." );
}
// stop all threads again
writeToStdout( "Terminating live threads..." );
s_boTerminated = true;
for( auto& it : threads )
{
it.second->join();
}
return 0;
}
Grants access to devices that can be operated by this software interface.
Definition mvIMPACT_acquire.h:7159
unsigned int deviceCount(void) const
Returns the number of devices currently present in the system.
Definition mvIMPACT_acquire.h:7388
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:6550
void open(void)
Opens a device.
Definition mvIMPACT_acquire.h:6419
PropertyIDeviceInterfaceLayout interfaceLayout
An enumerated integer property which can be used to define which interface layout shall be used when ...
Definition mvIMPACT_acquire.h:6643
PropertyIAcquisitionStartStopBehaviour acquisitionStartStopBehaviour
An enumerated integer property defining the start/stop behaviour during acquisition of this driver in...
Definition mvIMPACT_acquire.h:6788
The function interface to devices supported by this interface.
Definition mvIMPACT_acquire.h:10746
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
int getErrorCode(void) const
Returns a unique numerical representation for this error.
Definition mvIMPACT_acquire.h:275
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 information about a captured buffer.
Definition mvIMPACT_acquire.h:8628
bool isOK(void) const
Convenience function to check if a request has been processed successfully.
Definition mvIMPACT_acquire.h:9462
PropertyIRequestResult requestResult
An enumerated integer property (read-only) defining the result of this request.
Definition mvIMPACT_acquire.h:9768
Contains basic statistical information.
Definition mvIMPACT_acquire.h:14497
A class that can be used to display images in a window.
Definition mvIMPACT_acquire_display.h:587
A class that can be used for displaying images within existing windows or GUI elements that can provi...
Definition mvIMPACT_acquire_display.h:176
void SetImage(const void *pData, int width, int height, int bitsPerPixel, int pitch)
Sets the next image to display.
Definition mvIMPACT_acquire_display.h:296
void Update(void) const
Immediately redraws the current image.
Definition mvIMPACT_acquire_display.h:385
TDMR_ERROR
Errors reported by the device manager.
Definition mvDriverBaseEnums.h:2601
@ DMR_NO_ERROR
The function call was executed successfully.
Definition mvDriverBaseEnums.h:2603
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