Impact Acquire SDK .NET
Acquiring Data

The image acquisition using Impact Acquire with Balluff hardware is handled framework-side by these queues:

  • the request object queue
  • the request queue
  • the result queue

The main advantage of using queues is the independence of the capture process from side effects. Whenever the application and/or the operating system gets stuck as it is busy doing other things, in the background the framework can continue to work when it has image requests to process. Thus, short time intervals where the system doesn't schedule the applications threads can be buffered.

The following figure shows what the initial state of the framework's internal queues will look like when no request for an image has been made by the user:

Figure 1: Initial state

Within the user application, you can control the number of request objects with these functions:

The Capture Process

Step 1: Single Request

Whenever the user application requests an image, one of the request objects is placed in the request queue.

In device specific interface layout (see property InterfaceLayout of the device object or the enumeration mv.impact.acquire.Device.interfaceLayout for details) during this step all the settings (e.g. gain, exposure time, ...) that shall be used for this particular image request will get buffered, thus the framework will guarantee that these settings will be used to process this request.

In contrast to that when working with the GenICam™ interface layout, all modifications applied to settings will directly be written to the device thus in order to guarantee that a certain change is already reflected in the next image, the capture process must be stopped, then the changes must be applied and the capture process is restarted again.

Note
It is crucial to understand this fundamental difference between the two interface layouts! Please make sure you read the corresponding section of this manual: 'GenICam' vs. 'DeviceSpecific' Interface Layout

The function to send the request for one image down to the framework is named:

mv.impact.acquire.FunctionInterface.imageRequestSingle

Figure 2: mv.impact.acquire.FunctionInterface.imageRequestSingle
Note
There is NO live mode. To implement a continuous capture it is recommended that the user application starts a thread that continuously requests images and returns the capture images or the result of an image processing back to the application.
See also
The ContinuousCapture example in the examples section of this documentation

Step 2: Image Capture

Whenever there are outstanding requests in the request queue the framework will always automatically remove the oldest request from this queue when it is either done processing the previous request OR was in idle mode. It will then try to capture data according to the desired capture parameters (such as gain, exposure time, etc.) into the buffer associated with this request.

Figure 3: The acquisition

Step 3: Result Queue

When the framework stops processing the current request object it locks the content (and therefore also the attached image) and thus guarantees not to change it until it is unlocked by the user again.

Note
The term 'stops processing' does NOT necessarily guarantee that an image has been captured. There might be error conditions that don't allow a complete image to be captured or timeout conditions that will stop processing the current request (see section 'Timeouts' later in this chapter). A buffer placed in the result queue can still be incomplete or corrupt. Each request object therefore has its very own Result property that carries additional information about the consistency of the buffer content that has been delivered.
Figure 4: Request queue

Step 4: Request Wait For

When the user starts to wait for a request, the next available result queue entry will be returned to the application. The wait is a blocking function call. The function will return when either a request has been placed in the result queue by the framework or the user supplied timeout passed to the wait function call has elapsed. If there is at least one entry in the result queue already when the wait function is called, the function will return immediately. In order to check if there are entries in the result queue without waiting an application can pass 0 as the timeout value. However when the result queue is NOT empty, this call will remove one entry and return it to the application as would happen when passing a timeout value larger than 0, thus the application must always handle requests returned by the wait function. requests extracted from the result queue but NOT explicitly unlocked by the application will not be usable by the framework anymore!

This can be achieved by calling the function:

mv.impact.acquire.FunctionInterface.imageRequestWaitFor

Figure 5: mv.impact.acquire.FunctionInterface.imageRequestWaitFor

Step 5: Request Unlock

Request objects returned to the user are locked for the framework. So if the user does not unlock the images, the framework can't use this request object anymore and thus a permanent acquisition won't be possible as sooner or later all available requests will have been processed by the framework and have been returned to the user. Therefore, it is crucial to unlock request objects that have been processed by the user in order to allow the framework to use them again. This mechanism makes sure that the framework can't overwrite stuff the user still has to process and also makes sure that the user can't write into memory the framework will use to capture images into.

Note
Once unlocked the memory associated with the image is not guaranteed to remain valid, so it should not be accessed by the user application under any circumstances!

The functions to unlock the request buffer are called:

mv.impact.acquire.FunctionInterface.imageRequestUnlock

Figure 6: mv.impact.acquire.FunctionInterface.imageRequestUnlock

Reset The Queues

To restore the framework internal initial state (remove all queue entries from the chain of processing) this method can be used:

mv.impact.acquire.FunctionInterface.imageRequestReset

Note
Calling this function will NOT unlock requests currently locked by the user. A request object is locked for the framework whenever the corresponding wait function returns a valid request object. These objects must always be unlocked by the user explicitly.
Figure 7: Running capture process
Figure 8: After a call to mv.impact.acquire.FunctionInterface.imageRequestReset and unlocking the locked request objects

When not explicitly passing the request objects number to the framework when calling the imageRequestSingle function, the framework will use any of the request objects currently available to him, thus the order of the request objects might change randomly. An application should NOT rely on request objects arriving in a certain order unless explicitly stated by the application itself.

Timeouts

This section explains the different timeouts conditions that can occur during the capture process.

Two different timeouts have influence on the acquisition process. The first one is defined by the property ImageRequestTimeout_ms. This can be accessed like this:

mv.impact.acquire.BasicDeviceSettings.imageRequestTimeout_ms

This property defines the maximum time the framework tries to process a request. If this time elapses before e.g. an external trigger event occurred or an image was transmitted by the camera the request object will be returned to the framework. In that case, the framework will place this request object in the result queue and the user will get a valid request object when calling the corresponding 'wait for' function, but this request then will NOT contain a valid image, but it result property will contain

mv.impact.acquire.TRequestResult.rrTimeout.

Note
Since Impact Acquire 2.32.0 the default value for this property has been changed! See Calling 'imageRequestWaitFor' Never Returns / An Application No Longer Terminates After Upgrading To Impact Acquire 2.32.0 Or Greater for details.

The other timeout parameter is the timeout value passed to the 'wait for' function (described above). This value defines the maximum time in ms the function will wait for a valid image. After this timeout value elapsed, the function will return, but will not necessarily return a request object when no request object has been placed in the result queue.

Now assuming ImageRequestTimeout_ms is set to a value of 2000 ms the figure shows the status after, this is a triggered application and

Then the request queue will contain one entry:

Figure 9: Single request in a triggered application BEFORE trigger event

If now the next external trigger signal occurs AFTER the timeout of 2000ms has elapsed (or a trigger signal never occurs) the framework will place the unprocessed request object in its result queue.

Figure 10: Trigger event after exceeded default timeout

From here, it will be returned to the user immediately the next time mv.impact.acquire.FunctionInterface.imageRequestWaitFor is called.

mv.impact.acquire.Request.requestResult will contain mv.impact.acquire.TRequestResult.rrTimeout (figure 11) now

Figure 11: Image request returns without a valid image
Note
If no further call to mv.impact.acquire.FunctionInterface.imageRequestSingle has been made, the next triggered image will be lost, as the framework will silently discard the trigger signal because he has no request to process.

If the next external trigger comes BEFORE the timeout of 2000ms has elapsed, an image will be captured and the next call to mv.impact.acquire.FunctionInterface.imageRequestWaitFor will return after the image is ready, passing a valid image back to the user.

mv.impact.acquire.Request.requestResult will contain mv.impact.acquire.TRequestResult.rrOK.

Note
All requests returned by mv.impact.acquire.FunctionInterface.imageRequestWaitFor need to be unlocked in ANY case independent from mv.impact.acquire.Request.requestResult.

It's important to realize that even several calls to

mv.impact.acquire.FunctionInterface.imageRequestWaitFor

are possible to wait for a single request object. Until no image has been placed in the result queue each call will return after its timeout has elapsed returning

mv.impact.acquire.TDMR_ERROR.DEV_WAIT_FOR_REQUEST_FAILED

. However, waiting multiple times for an image has NO effect on the position of the request objects in their queues. This is only when either an image has been captured or when the timeout defined by the property ImageRequestTimeout_ms has elapsed.

Acquisition Start/Stop Behaviour

A couple of other features need to be mentioned when explaining Impact Acquire's buffer handling and data acquisition behaviour:

Some devices (e.g. GigE Vision™ devices) use a streaming approach to send data to the host system. This means once started the device will send its data to the host system as it becomes ready and does NOT wait for the host system to ask for data (such as an image). For the host system that can result in data being lost if it does not provide a sufficient amount of buffers to acquire the data into. Thus whenever a device is generating images, frames or data buffers faster than the host system can provide capture buffers, data is lost.

Some device/framework combinations therefore support 2 different ways to control the start/stop behaviour of data streaming from a device:

  • An automatic mode where the device framework internally decides when to start and when to stop the device's streaming channel. In this mode the framework will automatically send a 'start streaming' command to the device once the first request command is issued using the mv.impact.acquire.FunctionInterface.imageRequestSingle function. When the request queue runs empty the framework will wait the amount of milliseconds defined by the AcquisitionIdleTimeMax_ms properties. If no new request command is issued during that time, the framework will automatically send a 'stop streaming' command to the device
  • A user controlled mode where the application must explicitly start and stop the data transmission from the device to the host system

While the automatic mode keeps the application code fairly simple, the user controlled mode will sometimes allow a much better control about the capture process. Especially for applications where NO data loss can be accepted, the user controlled mode might be the better choice even though it requires a bit of additional code on the application side.

If the user controlled mode is supported the AcquisitionStartStopBehaviour property's translation dictionary will contain the assbUser value. The default mode will be assbDefault, but if supported an application can switch to assbUser BEFORE the device is opened.

Note
Some devices (e.g. all devices belonging to mvBlueLYNX-X family) will ONLY support the application controlled mode. For the mvBlueLYNX-X this also comes with another restriction: All buffers that will be used to capture data into between a call to the acquisition start function and the acquisition stop function must be announced to the framework by calling the function mv.impact.acquire.FunctionInterface.imageRequestSingle BEFORE starting the acquisition thus when there are 6 request objects all these 6 objects should be announced and then the acquisition should be started. This is for performance reasons. Buffers announced after calling the acquisition start command will be rejected by the framework!
The property AcquisitionStartStopBehaviour will become read-only after the device has been opened.

Situations where an application controlled start and stop of the device's data transmission might be beneficial:

  • NO loss of data can be tolerated
  • an application runs at very high frame rates (Otherwise especially when requesting the first buffer data might be lost, as when internally starting the data streaming from device the request queue might immediately run empty again (there is just a single buffer at that moment) as the device might send frames faster than additional buffers get queued by the application)

When the application controls the start and stop of the streaming from the device some additional code must be added to an application:

Device pDev = getDeviceFromSomewhere();
try
{
// set the 'acquisitionStartStopBehaviour' BEFORE opening the device!
pDev.acquisitionStartStopBehaviour.write(TAcquisitionStartStopBehaviour.assbUser);
pDev.open();
}
catch (ImpactAcquireException e)
{
// this e.g. might happen if the same device is already opened in another process...
// handle error
}
// ... more code
FunctionInterface fi = new FunctionInterface(pDev);
// Send all requests to the capture queue. To modify the number of requests
// use the property mv.impact.acquire.SystemSettings.requestCount at runtime (note that some devices will
// only allow to modify this parameter while NOT streaming data!) or the property
// mv.impact.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)
{
// handle error
}
// Start the acquisition manually. This is to prepare the driver for data capture and tell the
// device to start streaming data
if ((result = (TDMR_ERROR)fi.acquisitionStart()) != TDMR_ERROR.DMR_NO_ERROR)
{
// handle error
}
// ... more code (the actual image acquisition and processing)
// Stop the acquisition manually
if ((result = (TDMR_ERROR)fi.acquisitionStop()) != TDMR_ERROR.DMR_NO_ERROR)
{
// handle error
}
// ... more code (clean up, free buffers, close device, ...
TDMR_ERROR
Errors reported by the device manager.
Definition mvDriverBaseEnums.cs:2375