Click or drag to resize

Manually Controlling State Caching

The code that Nimbus generates in response to settings on the State Caching Editor relies upon a series of state caching functions in the Nimbus Template Library. These functions interact with the state caching engine to check for the presence of a cached value, retrieve a cached value, or update a cached value. In some circumstances, the logic required to represent couplings between some driver properties is more complex than can be represented with the simple settings in the State Caching Editor. In such cases, the state caching functions can be used directly in method and property implementations to manually implement state caching.

Another reason manual state caching may be useful is in the implementation of high-level configuration functions. Often these functions accept multiple parameters and each parameter may correspond to a driver property that you would like to have cached. If the configuration function does not delegate to a property setter, then the manual state caching functions may have to be used to invalidate or update cache entries. For instance, a high-level configuration function may use semi-colon-separated SCPI commands to send multiple instrument settings at once, rather than calling driver property setters individually. Thus, the state caching logic in the property setters is bypassed and must be placed in the configure function manually.

Note Note

Some driver properties can use the built-in state caching mechanism programmed through the State Caching Editor while other properties can use manual state caching. However, it is important to turn off state caching in the State Caching Editor for any property that uses manual state caching. Otherwise, Nimbus will generate redundant (and incorrect) state caching code and the results will be unpredictable.

The logic used to manually implement state caching is, of course, user-defined. However, the basic steps in all manual state caching scenarios is the same. The steps can be summarized as follows:

  1. Use the GetOrCreateCacheEntry function to create an entry in the cache that is associated with a driver property. This function returns a CCacheEntry object that exposes methods that can be used to retrieve and update the value of the cache entry.

  2. Use the InvalidateCacheEntry function or the CCacheEntry::Invalidate function to implement user-defined couplings between properties. For instance, you may create a cache entry for the Bandwidth property and couple it to the Frequency property by calling InvalidateCacheEntry on the Bandwidth property inside the Frequency property setter. This would ensure that any time the frequency is set, the subsequent queries of the Bandwidth property retrieve the actual value from the instrument rather than the cached value.

  3. In the property getter for a cached property, use the CCacheEntry::GetValue function to retrieve the value from the cache if the value is valid.

  4. In the property setter for a cached property, use the CCacheEntry::ContainsValue function to first check if the cache already contains the same value as the one being set. If so, then the property setter implementation need do nothing further. If the new value is different from the cached value, then the implementation should send the new value to the instrument and subsequently call the CCacheEntry::UpdateValue function to update the property's cached value.

Manually Implementing State Caching in a Property Getter

The example code below shows how to manually implement the getter for a property that is being cached:

C++
HRESULT Acme4321::IAcme4321_get_Bandwidth(double* val)
{
   // Retrieve the cache entry for the Bandwidth property
   CCacheEntry<double>* pEntry = GetOrCreateCacheEntry<double>("IAcme4321.Bandwidth");

   // If the value is in the cache AND is valid, retrieve the value and return
   if (pEntry->GetValue(val))
      return S_OK;

   // If we get this far, then the cached value is not valid, so we
   // must communicate with the instrument to retrieve the value
   HRESULT hr = InstrQueryResponse(val);

   return hr;
}
Manually Implementing State Caching in a Property Setter

The example code below shows how to manually implement the setter for a property that is being cached:

C++
HRESULT Acme4321::IAcme4321_put_Bandwidth(double val)
{
   // Retrieve the cache entry for the Bandwidth property
   CCacheEntry<double>* pEntry = GetOrCreateCacheEntry<double>("IAcme4321.Bandwidth");

   // If the value is in the cache AND is valid, simply return since we don't need to send the instrument command
   if (pEntry->ContainsValue(val, GetModel()))
      return S_OK;

   // If we get this far, then the new value is not in the cache, so we
   // must communicate with the instrument to set the value
   HRESULT hr = InstrPrintCommand(val);

   if (SUCCEEDED(hr))
   {
      // The new value was successfully sent to the instrument, so update the cache
      pEntry->UpdateValue(val);
   }

   return hr;
}
Manually Implementing a Cache Coupling

The example code below shows how to manually implement a coupling between two properties. In this example, the Bandwidth property is being cached and is coupled to the Frequency property. The coupling is such that any new value set for the Frequency property that is greater than 10 MHz invalidates the cached value of the Bandwidth property. Thus, the invalidation logic appears in the Frequency property setter.

C++
HRESULT Acme4321::IAcme4321_put_Frequency(double val)
{
   // Only couple the properties if the frequency is greater than 10 MHz
   if (val > 10E6)
   {
      InvalidateCacheEntry("IAcme4321.Bandwidth");
   }

   HRESULT hr = InstrPrintCommand(val);

   return hr;
}
See Also

Download a complete CHM version of this documentation here.