Impact Acquire SDK .NET
ContinuousCaptureAllDevices.cs

The ContinuousCaptureAllDevices program is based on the ContinuousCapture 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 sample can be used to acquire live images from a particular device.
Program location
The source file ContinuousCaptureAllDevices.cs can be found under:
%INSTALLDIR%\apps\CSharp\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 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).

Note
The thread related functions are written with the Windows® API. Users working on other platforms therefore will have to replace these system calls by the corresponding functions calls appropriate on their platforms.
static void Main(string[] args)
{
mv.impact.acquire.LibraryPath.init(); // this will add the folders containing unmanaged libraries to the PATH variable.
if (DeviceManager.deviceCount < 1)
{
Console.WriteLine("Unable to continue! Press any key to end the program.");
Console.Read();
Environment.Exit(1);
}
string productFilter = "";
// scan command line
if (args.Length == 1)
{
for (int i = 0; i < args.Length; i++)
{
string param = args[i], key, value;
int keyEnd = param.IndexOf('=');
if ((!param.Contains("=")) || (keyEnd == param.Length))
{
Console.WriteLine("Invalid command-line parameter: '{0}' (ignored).", param);
}
else
{
key = param.Substring(0, keyEnd);
value = param.Substring(keyEnd + 1);
if ((key == "product") || (key == "p"))
{
productFilter = value;
}
else
{
Console.WriteLine("Invalid command-line parameter: '{0}' (ignored).", param);
}
}
}
}
else
{
Console.WriteLine("No command-line parameters specified. Available parameters:");
Console.WriteLine(" 'product' or 'p' to specify a certain product type. All other products will be ignored then");
Console.WriteLine(" a '*' serves as a wildcard.");
Console.WriteLine();
Console.WriteLine("USAGE EXAMPLE:");
Console.WriteLine(" ContinuousCaptureAllDevices p=mvBlue* ");
Console.WriteLine();
}
// store all device infos in a vector
// and start the execution of a 'live' thread for each device.
List<ThreadParameters> threadParams = new List<ThreadParameters>();
for (int i = 0; i < DeviceManager.deviceCount; i++)
{
if (DeviceManager.deviceList[i].product.read().Contains(productFilter))
{
// initialise display window
// IMPORTANT: It's NOT save to create multiple display windows in multiple threads!!!
// Therefore this must be done before starting the threads
string windowtitle = String.Concat("mvIMPACT_acquire sample, Device " + DeviceManager.deviceList[i].serial.read());
// store thread parameter for this device in the vector
threadParams.Add(new ThreadParameters(DeviceManager.deviceList[i], windowtitle));
Console.WriteLine("{0} ({1})", DeviceManager.deviceList[i].family.read(), DeviceManager.deviceList[i].serial.read());
}
}
if (threadParams.Count == 0)
{
Console.WriteLine("No device found that matches the product filter '{0}'! Unable to continue!", productFilter);
Environment.Exit(1);
}
// start live threads
foreach (ThreadParameters p in threadParams)
{
p.thread = new Thread(ContinuousCaptureAllDevices.liveThread);
p.thread.Start(p);
}
// now all threads will start running...
Console.WriteLine("Press [ENTER] to start the acquisition( the initialisation of the devices might take some time )");
Console.ReadKey();
// stop all threads again
Console.WriteLine("Terminating live threads...");
// wait until each live thread has terminated.
foreach (ThreadParameters p in threadParams)
{
p.terminated = true;
p.thread.Join();
}
}
A small helper class to administer various library search path related variables and paths.
Definition LibraryPath.cs:14
static void init()
Calling this method will add the folders containing unmanaged libraries to the systems library search...
Definition LibraryPath.cs:251
This namespace contains classes and functions belonging to the image acquisition module of this SDK.
Definition Enumerations.cs:2
Definition Enumerations.cs:2
Definition Enumerations.cs:2

Then after the device has been initialized 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 mv.impactacquire.SystemSettings.requestCount at runtime (note that some devices will
// only allow to modify this parameter while NOT streaming data!) or the property
// mv.impactacquire.Device.defaultRequestCount BEFORE opening the device.
TDMR_ERROR result = TDMR_ERROR.DMR_NO_ERROR;
while ((result = (TDMR_ERROR)fi.imageRequestSingle()) == TDMR_ERROR.DMR_NO_ERROR) { };
if (result != TDMR_ERROR.DEV_NO_FREE_REQUEST_AVAILABLE)
{
Console.WriteLine("'FunctionInterface.imageRequestSingle' returned with an unexpected result: {0}", result);
Console.WriteLine("({0})", ImpactAcquireException.getErrorCodeAsString(result));
}
// Start the acquisition manually if this was requested(this is to prepare the driver for data capture and tell the device to start streaming data)
if (pThreadParameter.pDev.acquisitionStartStopBehaviour.read() == TAcquisitionStartStopBehaviour.assbUser)
{
if ((result = (TDMR_ERROR)(fi.acquisitionStart())) != TDMR_ERROR.DMR_NO_ERROR)
{
Console.WriteLine("'FunctionInterface.acquisitionStart' returned with an unexpected result: {0}", result);
Console.WriteLine("({0})", ImpactAcquireException.getErrorCodeAsString(result));
}
}
// run thread loop
Request pRequest = null;
int timeout_ms = 500;
int requestNr = -1;
// 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 = -1;
while (!pThreadParameter.terminated)
{
// 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)
{
Console.WriteLine("Info from {0}: {1}: {2}, {3}: {4}, {5}: {6}", pThreadParameter.pDev.serial.read(),
statistics.framesPerSecond.name, statistics.framesPerSecond.readS(),
statistics.errorCount.name, statistics.errorCount.readS(),
statistics.captureTime_s.name, statistics.captureTime_s.readS());
}
}
else
{
Console.WriteLine("Error: {0}", 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
{
// If the error code is -2119(DEV_WAIT_FOR_REQUEST_FAILED), the documentation will provide
// additional information under TDMR_ERROR in the interface reference (
Console.WriteLine("imageRequestWaitFor failed ({0}, {1}, device {2})", requestNr, ImpactAcquireException.getErrorCodeAsString(requestNr), pThreadParameter.pDev.serial.read());
Console.WriteLine(", timeout value too small?");
}
}
TDMR_ERROR
Errors reported by the device manager.
Definition mvDriverBaseEnums.cs:2375
TAcquisitionStartStopBehaviour
Defines valid modes for acquisition start/stop behaviour.
Definition mvDriverBaseEnums.cs:76

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

pRequest = fi.getRequest(requestNr);

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:

fi.imageRequestUnlock(lastRequestNr);
Source code
using System;
using System.Collections.Generic;
using System.Threading;
#if USE_DISPLAY
#endif // #if USE_DISPLAY
using mv.impact.acquire.examples.helper;
// Unlike the respective C++ example, no special measures have been taken to ensure
// "synchronised" console output by multiple threads. This is not necessary, since the
// Console.WriteLine() method of the .NET Framework guarantees thread-safety by itself.
namespace ContinuousCaptureAllDevices
{
class ContinuousCaptureAllDevices
{
public class ThreadParameters
{
public Device pDev;
public Thread thread;
#if USE_DISPLAY
public ImageDisplayWindow window;
#endif // #if USE_DISPLAY
public bool terminated = false;
public ThreadParameters(Device Dev, string windowtitle)
{
pDev = Dev;
#if USE_DISPLAY
window = new ImageDisplayWindow(windowtitle);
#endif // #if USE_DISPLAY
}
public void terminateThread()
{
terminated = true;
}
}
public static void liveThread(Object pData)
{
ThreadParameters pThreadParameter = (ThreadParameters)pData;
Device pDev = pThreadParameter.pDev;
uint cnt = 0;
Console.WriteLine("Trying to open {0}", 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
FeatureAccess.conditionalSetProperty(pDev.interfaceLayout, TDeviceInterfaceLayout.dilGenICam);
// if this device offers a user defined acquisition start/stop behaviour
// enable it as this allows finer control about the streaming behaviour
FeatureAccess.conditionalSetProperty(pDev.acquisitionStartStopBehaviour, TAcquisitionStartStopBehaviour.assbUser);
pDev.open();
}
{
// this e.g. might happen if the same device is already opened in another process...
Console.WriteLine("An error occurred while opening the device {0}", pDev.serial.read());
Console.WriteLine("(error code: {0} ({1})). Terminating thread.", e.errorCode, e.errorCodeAsString);
Console.WriteLine("Press [ENTER] to end the application...");
return;
}
Console.WriteLine("Opened {0}", pDev.serial.read());
#if USE_DISPLAY
ImageDisplay display = pThreadParameter.window.imageDisplay;
#endif // #if USE_DISPLAY
// establish access to the statistic properties
Statistics statistics = new Statistics(pDev);
// create an interface to the device found
// 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 = TDMR_ERROR.DMR_NO_ERROR;
while ((result = (TDMR_ERROR)fi.imageRequestSingle()) == TDMR_ERROR.DMR_NO_ERROR) { };
if (result != TDMR_ERROR.DEV_NO_FREE_REQUEST_AVAILABLE)
{
Console.WriteLine("'FunctionInterface.imageRequestSingle' returned with an unexpected result: {0}", result);
Console.WriteLine("({0})", ImpactAcquireException.getErrorCodeAsString(result));
}
DeviceAccess.manuallyStartAcquisitionIfNeeded(pDev, fi);
// run thread loop
Request pRequest = null;
int timeout_ms = 500;
int requestNr = -1;
// 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 = -1;
while (!pThreadParameter.terminated)
{
// 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)
{
Console.WriteLine("Info from {0}: {1}: {2}, {3}: {4}, {5}: {6}", pDev.serial.read(),
statistics.framesPerSecond.name, statistics.framesPerSecond.readS(),
statistics.errorCount.name, statistics.errorCount.readS(),
statistics.captureTime_s.name, statistics.captureTime_s.readS());
}
#if USE_DISPLAY
# if CLR_AT_LEAST_3_DOT_5
// Extension methods are not supported by CLR versions smaller than 3.5 and this next function
// is therefore not available then.
display.SetImage(pRequest);
# else
// If extension methods are not available, the following function can be used instead. It is
// not as convenient and will only work for some pixel formats. For more complex pixel formats
// use other overloads of this method
display.SetImage(pRequest.imageData.read(), pRequest.imageWidth.read(), pRequest.imageHeight.read(), pRequest.imageBytesPerPixel.read() * 8, pRequest.imageLinePitch.read());
# endif
display.Update();
#endif // #if USE_DISPLAY
}
else
{
Console.WriteLine("Error: {0}", 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
}
//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.
// Console.WriteLine("imageRequestWaitFor failed ({0}, {1}), timeout value too small?", requestNr, ImpactAcquireException.getErrorCodeAsString(requestNr));
//}
}
DeviceAccess.manuallyStopAcquisitionIfNeeded(pDev, fi);
#if USE_DISPLAY
// stop the display from showing freed memory
display.RemoveImage();
#endif // #if 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
}
static void Main(string[] args)
{
mv.impact.acquire.LibraryPath.init(); // this will add the folders containing unmanaged libraries to the PATH variable.
{
Console.WriteLine("Unable to continue! Press any key to end the program.");
Console.Read();
Environment.Exit(1);
}
string productFilter = "";
// scan command line
if (args.Length >= 1)
{
for (int i = 0; i < args.Length; i++)
{
string param = args[i];
int keyEnd = param.IndexOf('=');
if ((!param.Contains("=")) || (keyEnd == param.Length))
{
Console.WriteLine("Invalid command-line parameter: '{0}' (ignored).", param);
}
else
{
string key = param.Substring(0, keyEnd);
string value = param.Substring(keyEnd + 1);
if ((key == "product") || (key == "p"))
{
productFilter = value;
}
else
{
Console.WriteLine("Invalid command-line parameter: '{0}' (ignored).", param);
}
}
}
}
else
{
Console.WriteLine("No command-line parameters specified. Available parameters:");
Console.WriteLine(" 'product' or 'p' to specify a certain product type. All other products will be ignored then");
Console.WriteLine(" a '*' serves as a wildcard.");
Console.WriteLine();
Console.WriteLine("USAGE EXAMPLE:");
Console.WriteLine(" ContinuousCaptureAllDevices p=mvBlue*");
Console.WriteLine();
}
// store all device infos in a vector
// and start the execution of a 'live' thread for each device.
List<ThreadParameters> threadParams = new List<ThreadParameters>();
for (int i = 0; i < DeviceManager.deviceCount; i++)
{
if (DeviceManager.deviceList[i].product.read().Contains(productFilter))
{
// initialise display window
// IMPORTANT: It's NOT safe to create multiple display windows in multiple threads!!!
// Therefore this must be done before starting the threads
string windowtitle = String.Concat("mvIMPACT_acquire sample, Device " + DeviceManager.deviceList[i].serial.read());
// store thread parameter for this device in the vector
threadParams.Add(new ThreadParameters(DeviceManager.deviceList[i], windowtitle));
Console.WriteLine("{0} ({1})", DeviceManager.deviceList[i].family.read(), DeviceManager.deviceList[i].serial.read());
}
}
if (threadParams.Count == 0)
{
Console.WriteLine("No device found that matches the product filter '{0}'! Unable to continue!", productFilter);
Environment.Exit(1);
}
// start live threads
foreach (ThreadParameters p in threadParams)
{
p.thread = new Thread(ContinuousCaptureAllDevices.liveThread);
p.thread.Start(p);
}
// now all threads will start running...
Console.WriteLine("Press [ENTER] to start the acquisition( the initialisation of the devices might take some time )");
Console.ReadKey();
// stop all threads again
Console.WriteLine("Terminating live threads...");
// wait until each live thread has terminated.
foreach (ThreadParameters p in threadParams)
{
p.terminated = true;
p.thread.Join();
}
}
}
}
Grants access to devices that can be operated by this software interface.
Definition DeviceManager.cs:157
static ReadOnlyCollection< Device > deviceList
Returns a list of all devices currently detected by the device manager.
Definition DeviceManager.cs:1030
static int deviceCount
Returns the number of devices currently present in the system.
Definition DeviceManager.cs:1064
This class and its functions represent an actual device detected by this interface in the current sys...
Definition Device.cs:91
readonly EnumPropertyI< TAcquisitionStartStopBehaviour > acquisitionStartStopBehaviour
An enumerated integer property defining the start/stop behaviour during acquisition of this driver in...
Definition Device.cs:731
readonly EnumPropertyI< TDeviceInterfaceLayout > interfaceLayout
An enumerated integer property which can be used to define which interface layout shall be used when ...
Definition Device.cs:604
void open()
Opens a device.
Definition Device.cs:209
readonly PropertyS serial
A string property (read-only) containing the serial number of this device.
Definition Device.cs:516
T read()
Reads a value from a property.
Definition EnumPropertyI.cs:342
The function interface to devices supported by this interface.
Definition FunctionInterface.cs:21
int imageRequestSingle()
Sends an image request to the mv.impact.acquire.Device driver.
Definition FunctionInterface.cs:656
Request getRequest(int nr)
Returns a const pointer to the desired mv.impact.acquire.Request.
Definition FunctionInterface.cs:452
int imageRequestUnlock(int nr)
Unlocks the request for the driver again.
Definition FunctionInterface.cs:987
int imageRequestWaitFor(int timeout_ms)
Waits for a request object to become ready.
Definition FunctionInterface.cs:1021
int imageRequestReset(int requestCtrlNr)
Deletes all requests currently queued for the specified mv.impact.acquire.ImageRequestControl.
Definition FunctionInterface.cs:575
bool isRequestNrValid(int nr)
Check if nr specifies a valid mv.impact.acquire.Request.
Definition FunctionInterface.cs:1098
An base class for exceptions generated by Impact Acquire.
Definition Exceptions.cs:9
static String getErrorCodeAsString(int errorCode)
Returns a string representation of a error.
Definition Exceptions.cs:48
String errorCodeAsString
Returns a string representation of the error associated with the exception.
Definition Exceptions.cs:118
int errorCode
Returns a unique numerical representation for this error.
Definition Exceptions.cs:101
IntPtr read()
Reads a value from a property.
Definition PropertyPtr.cs:49
String read()
Reads a value from a property.
Definition PropertyS.cs:144
Contains information about a captured buffer.
Definition Request.cs:77
readonly PropertyPtr imageData
A pointer property (read-only) containing the start address of the image data.
Definition Request.cs:1579
readonly PropertyI imageLinePitch
An integer property (read-only) containing the offset (in bytes) to the next line of each channel bel...
Definition Request.cs:1655
readonly EnumPropertyI< TRequestResult > requestResult
An enumerated integer property (read-only) defining the result of this request.
Definition Request.cs:1211
readonly PropertyI imageWidth
An integer property (read-only) containing the width of the image in pixels.
Definition Request.cs:1693
readonly PropertyI imageBytesPerPixel
An integer property (read-only) containing the number of bytes per pixel in this image.
Definition Request.cs:1679
bool isOK
Convenience function to check if a request has been processed successfully.
Definition Request.cs:1173
readonly PropertyI imageHeight
An integer property (read-only) containing the height of the image in pixels.
Definition Request.cs:1704
Contains statistical information.
Definition Statistics.cs:10
readonly PropertyI errorCount
An integer property (read-only) containing the overall count of image requests which returned with an...
Definition Statistics.cs:82
readonly PropertyF framesPerSecond
A float property (read-only) containing the current number of frames captured per second.
Definition Statistics.cs:98
readonly PropertyF captureTime_s
A float property (read-only) containing the average time needed to capture an image.
Definition Statistics.cs:75
A class that can be used to display images in a window.
Definition ImageDisplayWindow.cs:15
A class that can be used for displaying images within existing windows or GUI elements that can provi...
Definition ImageDisplay.cs:39
void RemoveImage()
Removes the current image from the display.
Definition ImageDisplay.cs:327
void SetImage(IntPtr pData, int width, int height, int bipp, int pitch)
Sets the next image to display.
Definition ImageDisplay.cs:282
void Update()
Immediately redraws the current image.
Definition ImageDisplay.cs:333
TDeviceInterfaceLayout
Defines valid interface layouts for the device.
Definition mvDriverBaseEnums.cs:1922
This namespace contains classes and functions that can be used to display images.
Definition Enumerations.cs:2