Click or drag to resize

Implementing IVI-C-Only Functions and Attributes

Nimbus allows any IVI-C function or attribute to be implemented manually. In general, there are two reasons one might implement an IVI-C function or attribute manually:

  1. To override the Nimbus auto-generated implementation.

  2. To implement an IVI-C-only function or attribute.

The techniques presented in this section apply equally well to both scenarios.

File Locations of IVI-C Functions and Attributes

By default, when an IVI-C function or attribute is associated with an IVI-COM method or property, Nimbus generates an implementation of the IVI-C function or attribute in the following file:

<prefix>.nimbus.cpp

The auto-generated implementation delegates to the associated IVI-COM method or property, automatically performing the requisite data type conversions, error translations, and other tasks. If the Implementation type of the IVI-C function or attribute is changed to Manual, then the implementation of the function or attribute is removed from the Nimbus-managed <prefix>.nimbus.cpp file. A stub implementation of the IVI-C function or attribute is then added to the following user-managed file:

<prefix>.cpp

Similarly, if an IVI-C-only function or attribute is added to the driver via the IVI-C Designer, then it is always placed in the user <prefix>.cpp file. Manual implementation is the only option for IVI-C-only functions and attributes.

All of the code for manually implementing an IVI-C function or attribute is always placed in the user <prefix>.cpp file.

Tip Tip

The best way to begin manually implementing an IVI-C function or attribute is to refer to the auto-generated code in the Nimbus-managed <prefix>.nimbus.cpp file. This code demonstrates many techniques that may be needed in your manual IVI-C implementation.

Manually Implementing an IVI-C Function

Though an IVI-C function implementation can use a multitude of techniques to fulfill its purpose, the two most common scenarios in Nimbus drivers involve using IVI-C attributes and delegating to IVI-COM methods. This section discusses both scenarios.

Implementing IVI-C Functions Using IVI-C Attributes

One of the most common scenarios for manually implementing IVI-C functions is high-level "configure" functions. By convention, IVI drivers typically define a Configure function to set the values of attributes. The syntax for directly reading and writing IVI-C attributes via attribute accessors is somewhat cumbersome for end users, so IVI recommends that Configure functions be made available as well.

The following example demonstrates how to implement an IVI-C function in terms of an IVI-C attribute:

C++
ViStatus _VI_FUNC acme4321_ConfigureAcquisitionType ( ViSession Vi, ViInt32 AcquisitionType )
{
    ViStatus status = VI_SUCCESS;

  status = acme4321_SetAttributeViInt32(vi, VI_NULL, ACME4321_ATTR_ACQUISITION_TYPE, AcquisitionType);

    return status;
}

Implementing IVI-C Functions Using IVI-COM Methods and Properties

As mentioned previously, the default mode for Nimbus is to implement IVI-C functions in terms of the associated IVI-COM method or property (except, of course, in the case of IVI-C-only functions). The best way to understand how to manually implement an IVI-C function in terms of IVI-COM methods and properties is by examining a Nimbus-generated implementation from the <prefix>.nimbus.cpp file.

The following code demonstrates how to implement the IVI-C function ConfigureEdgeTriggerSource in terms of the IVI-COM method IIviScopeTriggerEdge.Configure:

C++
ViStatus _VI_FUNC acme4321_ConfigureEdgeTriggerSource ( ViSession Vi, ViConstString Source, ViReal64 Level, ViInt32 Slope )
{
    ViStatus status = VI_SUCCESS;
    CComPtr<IIviScopeTriggerEdge> spTriggerEdge;
    status = GetDriverInterface(Vi, &spTriggerEdge);
    if (status == VI_SUCCESS)
    {
        CParam<ViConstString> _Source(Source);
        CParam<ViReal64> _Level(Level);
        CParam<ViInt32> _Slope(Slope);
        HRESULT hr = spTriggerEdge->Configure(_Source, _Level, (IviScopeTriggerSlopeEnum)(long)_Slope);
        status = TranslateHRESULT(Vi, hr);
    }

    return status;
}

The first task performed by the above code is to obtain a pointer to the appropriate interface on the IVI-COM driver. In this case, the Configure method we are after resides on the IIviScopeTriggerEdge interface. This interface is accessed by calling the GetDriverInterface helper function provided by Nimbus. The interface pointer is returned from this function and stored in a CComPtr smart pointer variable named spTriggerEdge. Using CComPtr ensures that the reference to the interface is automatically released when the function exits.

The code then checks the status returned from GetDriverInterface, and, if successful, it sets up the three temporary input parameters (_Source, _Level, and _Slope) that will be passed to the IVI-COM Configure method.

The three temporary parameters (declared as CParam objects) are templatized based on the IVI-C parameter type and serve a very important role -- they automatically perform the data type conversions from the IVI-C input parameters to their COM equivalents. For instance, the Source parameter passed to the acme4321_ConfigureEdgeTriggerSource function is of type ViConstString. However, the corresonding IVI-COM method accepts the COM BSTR data type for its Source parameter. The IVI-C parameter cannot simply be passed directly to the IVI-COM method -- it must be converted from ViConstString to BSTR. This task is performed by the CParam helper classes provided by Nimbus. The CParam class can be used to convert between any two compatible IVI-C and IVI-COM data types.

With the appropriate CParam objects declared, the IVI-COM Configure method is invoked using the spTriggerEdge smart pointer. IVI-COM methods always return an HRESULT to indicate success or failure. They also populate a "hidden" COM error object that contains additional error information, such as a string description of the error that occurred. This information must be translated into a form that IVI-C clients can consume. This is the role of the TranslateHRESULT helper function provided by Nimbus. This function converts the HRESULT into a corresponding IVI-C ViStatus value. It also harvests the error description from the COM error object and stores it in the IVI-C session so that client calls to the IVI-C GetError function will return the appropriate message.

Returning Errors from IVI-C Functions

There are two techniques used for returning errors from IVI-C functions. The TranslateHRESULT function described above is always used when the IVI-C function is implemented by delegating to an IVI-COM method or property. The other option is to use the ReportError function with an explicit IVI-C error name and description.

The following code demonstrates use of the ReportError function to validate parameters passed to an IVI-C function.

C++
ViStatus _VI_FUNC acme4321_FetchWaveformMeasurement ( ViSession Vi, ViConstString Channel, ViInt32 MeasFunction, ViReal64* Measurement )
{
    if (Measurement == VI_NULL) return ReportError(Vi, ACME_ERROR_NULL_POINTER, _T("Parameter Measurement is VI_NULL."));

    ViStatus status = VI_SUCCESS;

    // ...

    return status;
}

The code in the above example uses ReportError to report a NULL-pointer error for the Measurement output parameter.

Manually Implementing an IVI-C Attribute

IVI-C attributes are implemented using the same techniques as IVI-C functions. The additional requirement for IVI-C attributes is that their implementation functions follow a precise naming convention. IVI-C attributes must be implemented using two functions (a getter and a setter) if they are read-write attributes. Only a single function is supplied for read-only or write-only attributes.

An IVI-C attribute getter must be implemented with a function declaration of the following form:

ViStatus _VI_FUNC <prefix>_get_<ATTR_NAME> ( ViSession Vi, ViConstString RepCapIdentifier, <AttrType>* AttributeValue)

An IVI-C attribute setter must be implemented with a function declaration of the following form:

ViStatus _VI_FUNC <prefix>_set_<ATTR_NAME> ( ViSession Vi, ViConstString RepCapIdentifier, <AttrType> AttributeValue)

For example, the following code shows stub implementations for the getter and setter functions for the IVI-C attribute TRIGGER_SLOPE. Note that the full attribute name as declared in the #define statement in the <prefix>.h file is ACME4321_ATTR_TRIGGER_SLOPE. However, only the TRIGGER_SLOPE portion of the full attribute name is used in the getter and setter implementation declarations.

C++
ViStatus _VI_FUNC acme4321_get_TRIGGER_SLOPE ( ViSession Vi, ViConstString RepCapIdentifier, ViInt32* AttributeValue )
{
  // ...
}

ViStatus _VI_FUNC acme4321_set_TRIGGER_SLOPE ( ViSession Vi, ViConstString RepCapIdentifier, ViInt32 AttributeValue )
{
  // ...
}
Session Locking in IVI-C Functions and Attributes

IVI-C drivers support a feature called session locking. Session locking allows a client program to call the <prefix>_LockSession function to gain exclusive access to the instrument session. No calls from other threads should execute on that instrument session until the client calls <prefix>_UnlockSession.

Note Note

Session locking does not prevent multiple threads from accessing different sessions connected to the device over different communication ports. For instance, if two sessions are created using the same driver by calling <prefix>_init with different resource descriptors, then session locking will not prevent multiple threads from executing on the two sessions, even if they refer to the same physical instrument.

Since sessions cannot be shared across processes, this also means that session locking does not protect against simultaneous instrument access from multiple applications.

When manually implementing IVI-C functions and attributes, session locking is automatically implemented within the Nimbus Template Library if the manual implementation delegates to the underlying IVI-COM driver. As demonstrated above, delegating to any IVI-COM driver method involves calling the GetDriverInterface helper function, and this function calls the appropriate session locking functions. However, if your manual IVI-C function or attribute implementation does not rely upon the IVI-COM driver (it does not call GetDriverInterface as one of its first actions), then you must use the CSessionLock utility class to properly implement session locking.

The example below demonstrates how to use the CSessionLock class to implement session locking in an IVI-C function that does not rely upon the IVI-COM core driver.

C++
ViStatus _VI_FUNC acme4321_FetchWaveformMeasurement ( ViSession Vi, ViConstString Channel, ViInt32 MeasFunction, ViReal64* Measurement )
{
    CSessionLock lock(Vi);  // The ctor obtains the lock and the dtor releases the lock.
                          // If the calling thread is not the lock owner, then the calling
                          // thread will block until the lock owner released the lock.

    if (Measurement == VI_NULL) return ReportError(Vi, ACME_ERROR_NULL_POINTER, _T("Parameter Measurement is VI_NULL."));

    ViStatus status = VI_SUCCESS;

    // ...

    return status;
}
See Also

Download a complete CHM version of this documentation here.