Main Content

Implementing Get and Set Support for Device-Specific Properties

After connecting to a device through your adaptor, users might want to view or modify values of the properties of the device. For example, a user might adjust the value of the Brightness property or retrieve the current value of the Temperature property. (For information about how to define the properties you want to expose to users, see Creating Device Properties.)

To receive notification from the engine when a user wants to view or modify a property, associate a listener object with the property. The toolbox defines two types of listener classes: get listeners that respond to get commands and set listeners that respond to set commands.

To provide support for getting and setting property values, follow this procedure:

  1. Define a listener class of the appropriate type. The toolbox defines two abstract classes, one for get listeners and one for set listeners, from which you derive your class.

  2. Implement the virtual function required by the class.

  3. Associate an instance of your listener class with the property.

The following sections describe how to set up get listeners and set listeners and in your adaptor.

Setting Up Get Listeners in Your Adaptor

To receive notification from the engine when a user requests the current value of a property using the get command:

  1. Define a get listener class, deriving it from the IPropCustomGetFcn abstract class—see Defining a Get Listener Class.

  2. Implement the getValue() virtual function in your listener class—see Creating the getValue() Function for Your Class.

  3. Associate an instance of your listener class with a property—see Associating Get Listeners with Properties.

Defining a Get Listener Class

Create a get listener class, deriving it from the abstract class IPropCustomGetFcn, as shown in the following example.

In this example, the constructor accepts a handle to an IAdaptor object. Because the toolbox establishes listeners on a per-instance basis, passing this handle can be helpful, but it is not a requirement.

The IPropCustomGetFcn class defines one virtual function: the getValue() member function. In this function, you define how your adaptor responds when a user requests the current value of a property. For more information about the getValue() function, see Creating the getValue() Function for Your Class.

#include "mwadaptorimaq.h" 
#include "MyDeviceImaq.h" // For this example

class MyDevicePropGetListener : public IPropCustomGetFcn
{
public: 

   // Constructor/Destructor
	MyDevicePropGetListener(MyDeviceAdaptor* parent):
                               _parent(parent) {}

  virtual ~MyDevicePropGetListener() {};

  virtual void getValue(imaqkit::IPropInfo* propertyInfo,
                                      void* value);

private:

   // Declare handle to parent as member data.
   MyDeviceAdaptor* _parent;

};

Creating the getValue() Function for Your Class

When a user requests the current value of a property, the engine calls the getValue() function of the get listener class associated with the property.

Your getValue() function must accept two parameters:

void getValue(IPropInfo* propertyInfo, void* value)
  • propertyInfo is a handle to an IPropInfo object.—The IPropInfo class is the interface that lets you get information about the property. For example, using IPropInfo functions you can retrieve the property name, its storage type and its default value. This information is useful if you have a generic listener class that handles multiple properties.

  • value is a pointer to the location in memory where your adaptor stores the requested property value.—The engine passes this value as a void*. Your getValue() function must cast the value to the appropriate C++ data type. The following table tells which C++ data type to cast to for all property types supported by the adaptor kit.

    imaqkit::PropertyTypes

    C++ Data Type

    CHARACTER_VECTOR

    char**

    DOUBLE

    double*

    INT

    int*

    DOUBLE_ARRAY

    imaqkit::PropertyTypes::NDoubles*

    INT_ARRAY

    imaqkit::PropertyTypes::NInts*

    For nonscalar data types, character vectors, double arrays, and integer arrays, your listener class must allocate sufficient memory for the current property value using the new[] operator. The engine deletes this memory, calling delete[]. An example using a character vector property is:

    char** returnStr = reinterpret_cast<char**>(value);
    *returnStr = imaqkit::imaqmalloc(sizeof(char) * (stringLength));
    strcpy(*returnStr, currentPropertyValueString);
    

Suggested Algorithm for a getValue() Function.  The design of the getValue() function varies with the needs of your device and the facilities offered by its SDK. For example, you could create one get listener class that handles value queries for all properties in a particular property container (general or device-specific). In this case, the getValue() function includes a switch statement with cases that handle each individual property.

Alternatively, define a separate get listener class for each property or each property storage type. Then, the engine calls the specific listener for the property specified.

You also can define get listener classes that fit the way the device SDK organizes property configuration. For example, if an SDK provides one function to configure all device properties, you can define a get listener class for these properties.

Example.  This example shows an implementation of a getValue() function for integer types:

void MyDevicePropGetListener::getValue(IPropInfo* propertyInfo,
                                            void* value)
{
    
   // Get property name from the IPropInfo object.
   const char* propname = propertyInfo->getPropertyName();

   // Get the value using whatever facility your device's SDK provides.
   *reinterpret_cast<const int*>(value) = sdk_function_get();

   // For debug purposes only.
   imaqkit::adaptorWarn("In listener. Property name is %s\n",propname);
}

Associating Get Listeners with Properties

To set up a listener for a property, you associate the listener object with the property in the property container. The following example shows how to add get listeners for all device-specific properties in the adaptor property container. Adaptor writers typically set up property listeners in their adaptor class constructor—see Implementing Your Adaptor Class Constructor.

  1. Get a handle to the appropriate property container object.

    The IEngine object has two member functions that return handles to property containers (IPropContainer objects). The example calls the IEngine class getAdaptorPropContainer() member function to get the device-specific property container:

    imaqkit::IPropContainer* adaptorPropContainer =
                             getEngine()->getAdaptorPropContainer();
    
  2. Add a get listener to a property in the container, using the IPropContainer object setCustomGetFcn() function. As arguments, specify the property name and a handle to the listener object.

    Note

    Because the toolbox deletes each instance of a listener object when a user deletes the video input object, associate a new instance of a listener object with each property.

    The following example iterates through all properties in the adaptor property container, associating a get listener object with each one.

    void MyDeviceAdaptor::MyDeviceAdaptor()
    {
        // get a handle to the property container
        IPropContainer* propContainer = 
                           getEngine()->getAdaptorPropContainer();
    
        // Determine the number of properties in the container.
        int numDeviceProps = propContainer->getNumberProps();
    
        // Retrieve the names of all the properties in the container
        const char **devicePropNames = new const
                           char*[numDeviceProps];
        propContainer->getPropNames(devicePropNames);
    
        // Create a variable to point to a property get listener object.
        MyDevicePropGetListener* getListener;
    
        // For each property in the container...
        for (int i = 0; i < numDeviceProps; i++){
    
            // Create a get listener object...
            getListener = new MyDevicePropGetListener(this);
    
            // and associate it with a specific property.
            propContainer->setCustomGetFcn(devicePropNames[i], getListener);
        }
    
        // clean up the array of property names.
    
        delete [] devicePropNames;
        
    }
    

Setting Up Set Listeners in Your Adaptor

To receive notification from the engine when a user changes the value of a property using the set command:

  1. Define a set listener class, deriving it from the IPropPostSetListener abstract class—see Defining a Set Listener Class.

  2. Implement the notify() virtual function in your set listener class—see Creating the notify() Function for Your Class.

  3. Associate an instance of your set listener class with the property—see Associating Set Listeners with Properties.

Defining a Set Listener Class

Create a set listener class, deriving it from the abstract class IPropPostSetListener, as shown in the following example. (The name of the class includes the word Post because the toolbox notifies listeners after it updates the property value stored in the container.)

In this example, the constructor accepts a handle to an IAdaptor object. Because the toolbox establishes listeners on a per-instance basis, passing this handle can be helpful, but it is not a requirement.

The IPropPostSetListener class defines one virtual function: the notify() member function. In this function, you define how your adaptor responds when a user changes the value of a property. For more information, see Creating the notify() Function for Your Class.

#include "mwadaptorimaq.h" 
#include "MyDeviceImaq.h" // For this example

class MyDevicePropSetListener : public IPropPostSetListener
{
public: 

   // Constructor/Destructor
	MyDevicePropSetListener(MyDeviceAdaptor* parent):
                               _parent(parent) {}

  virtual ~MyDevicePropSetListener() {};

  virtual void notify(imaqkit::IEnginePropInfo* propertyInfo,
                       void* newValue);

private:

   // Declare handle to parent as member data
   MyDeviceAdaptor* _parent;

   // Property Information object.
   imaqkit::IPropInfo* _propInfo;

   // The new value for integer properties.
   int _lastIntValue;

   // The new value for double properties.
   double _lastDoubleValue;

   // The new value for character vector properties.
   char* _lastStrValue;

};

Creating the notify() Function for Your Class

When a user calls the set command to change the value of a property, the engine calls the notify() function of the set listener class associated with the property.

A set listener class notify() function must accept two parameters:

void notify(IPropInfo* propertyInfo, void* newValue)
  • propertyInfo is a handle to an IPropInfo object—The IPropInfo class is the interface that lets you get information about the property. For example, using IPropInfo functions you can get the property name, its storage type, and its default value.

  • newValue is a pointer to the new property value—This engine passes this value as a void*. Your notify() function must cast the value to the appropriate C++ data type. The following table tells which C++ data type to cast to for all property types supported by the adaptor kit.

    imaqkit::PropertyTypes

    C++ Data Type

    CHARACTER_VECTOR

    char*

    DOUBLE

    double*

    INT

    int*

    DOUBLE_ARRAY

    imaqkit::PropertyTypes::NDoubles*

    INT_ARRAY

    imaqkit::PropertyTypes::NInts*

Suggested Algorithm for notify() Function.  The design of the notify() function varies with the needs of your device and the facilities offered by its SDK. For example, you can create one set listener class that handles all value changes for all properties in a particular property container (general or device-specific). In this case, the notify() function includes a switch statement with cases that handle each individual property.

Alternatively, you could define a separate set listener class for each property or each property storage type. Then, the engine calls the specific listener for the property specified.

You also can define set listener classes that fit the way the SDK organizes property configuration. For example, if an SDK provides one function to configure all device properties, you can define a set listener class for these properties.

Example.  This example shows an implementation of a notify() function for integer types:

void MyDevicePropSetListener::notify(IPropInfo* propertyInfo,
                                         void* newValue)
{
    
   // Get property name from the IPropInfo object.
   const char* propname = propertyInfo->getPropertyName();

   // Cast newValue to the proper type
   newVal = *reinterpret_cast<const int*>(newValue);

   // *****************************************************
   // Insert calls to device SDK to apply value to hardware.
   // *****************************************************

   // For debug purposes only.
   imaqkit::adaptorWarn("In listener. Property name is %s\n",propname);

}

Associating Set Listeners with Properties

To set up a listener for a property, you associate the listener object with the property in the property container. The following example shows how to add set listeners for all the device-specific properties in the adaptor property container. Adaptor writers typically set up property listeners in their adaptor class constructor—see Implementing Your Adaptor Class Constructor.

  1. Get a handle to the appropriate property container object.

    The IEngine object has two member functions that return handles to property containers (IPropContainer objects). The example calls the IEngine class getAdaptorPropContainer() member function to get the device-specific property container:

    imaqkit::IPropContainer* adaptorPropContainer =
                             getEngine()->getAdaptorPropContainer();
    
  2. Add a set listener to a property in the container, using the IPropContainer object's addListener() function. As arguments, specify the property name and a handle to the listener object.

    Note

    Because the toolbox deletes each instance of a listener object when a user deletes the video input object, associate a new instance of a listener object with each property.

    The following example iterates through all properties in the adaptor property container, associating a set listener object with each property:

    void MyDeviceAdaptor::MyDeviceAdaptor()
    {
        // get a handle to the property container
        IPropContainer* propContainer = 
                           getEngine()->getAdaptorPropContainer();
    
        // Determine the number of properties in the container.
        int numDeviceProps = propContainer->getNumberProps();
    
        // Retrieve the names of all the properties in the container
        const char **devicePropNames = new const
                           char*[numDeviceProps];
        propContainer->getPropNames(devicePropNames);
    
        // Create a variable to point to a property listener object.
        MyDevicePropSetListener* setListener;
    
        // For each property in the container...
        for (int i = 0; i < numDeviceProps; i++){
    
            // Create a set listener object...
            setListener = new MyDevicePropSetListener(this);
    
            // and associate it with a specific property.
            propContainer->addListener(devicePropNames[i], setListener);
        }
    
        // clean up the array of property names.
    
        delete [] devicePropNames;
        
    }