Impact Acquire SDK Python
Important Differences Between The C++ API And The Python API

When you are familiar with the C++ interface of Impact Acquire already there are some of important differences you need to know! They shall be explained briefly:

Note
The API is currently tested using Python 3.8.10 (Impact Acquire < 3.0.1 was tested using Python 3.7.4 and Python 2.7.2.1). If you encounter issues with this or other versions of Python you are working with please get in touch! However, Balluff does not intend to support Python versions that receive no support from the Python Software Foundation (PSF). In particular, Impact Acquire >= 3.0.1 does not support any version of Python 2. See the Python Developer's Guide for more information on the PSF's supported versions and maintenance schedules.
When using Python >= 3.12, Impact Acquire >= 3.0.1 is required.

There are not too many differences between the C++ and the Python API but some of them are important. They shall be explained briefly:

  • Stuff that has already been declared deprecated at the time of publishing the Python API will not be available
  • Enumerations from the C++ interface do NOT declare a new type in Python. Since an enum value in C/C++ is accessible through the enclosing namespace in Python this enum value will become a module wide constant. Accessing the enumeration value will work like in C/C++:
      // C++
      const TInterfaceLayout interfaceLayout = mvIMPACT::acquire::dilGenICam;
      # Python
      interfaceLayout = mvIMPACT.acquire.dilGenICam
    
    However in Python there will be no type TInterfaceLayout. As most of the documentation in this manual has been auto-translated from the C++ API reference there will be some locations where these enum types are still referenced. Valid enumeration values in this case can be derived from the building a matching prefix yourself. Take all the capital letters from the type (EXCLUDING the first 'T') and check the documentation for matching values. For the type TDeviceInterfaceLayout this prefix will be dil!
  • Simple getter functions may be wrapped as Python properties to have a more Python-like interface. So e.g. the function Component::isValid() will be a property (Component.isValid) in Python
  • Code that in C++ resides in sub-namespaces like e.g. mvIMPACT::acquire::GenICam or mvIMPACT::acquire::display will all end up in acquire in Python(this is likely to change in future versions as the reason for this is a SWIG limitation!)
  • Some functions that use Python style built-in names like mvIMPACT::acquire::Component::type() will use a slightly different name in Python like getType in order to avoid confusion
  • As SWIG has trouble to generate proper code when a C++ class has a member function and a static member function with the same name the static versions of these functions have been prefixed with get. So you call
      c = acquire.Component(hObj)
      # member function
      c.visibilityAsString()
      # static member function
      acquire.Component.getVisibilityAsString(acquire.cvGuru)
    

Apart from that if someone is familiar with the C++ interface it shouldn't be too difficult to use Impact Acquire with Python.

Aligning Raw Buffers to API Requirements

Some APIs like mvIMPACT::acquire::Request::attachUserBuffer require a buffer aligned to a certain size. In Python, objects supporting the Buffer protocol can be used for these APIs (e.g. bytearray), but there is no convenient way of fulfilling the alignment requirement. A useful method of achieving arbitrary alignment is to use the ctypes module to obtain the address of the underlying buffer and offset it to match the alignment:

size = getBufferSizeFromSomewhere()
alignment = getBufferAlignmentFromSomewhere()
import ctypes
# Maximum amount of extra memory required - we can't know exactly before allocating!
requiredOversize = size + alignment - 1
oversizeBuffer = bytearray(requiredOversize)
# Get the address of the bytearray's backing buffer
oversizeCType = ctypes.c_char * requiredOversize
oversizeMemory = oversizeCType.from_buffer(oversizeBuffer)
oversizeAddress = ctypes.addressof(oversizeMemory)
# Calculate required offset into oversized buffer to reach proper alignment
offsetToAligned = alignment - oversizeAddress % alignment
# Create a raw C buffer with offset to not copy the byte array in the process
bufferCType = ctypes.c_char * (requiredOversize - offsetToAligned)
correctRawCMemory = bufferCType.from_buffer(oversizeMemory, offsetToAligned)
# Call the API you need...
request = getRequestFromSomewhere()
request.attachUserBuffer(correctRawCMemory)