Main Page | Class Hierarchy | Class List | File List | Class Members | File Members | Related Pages | Examples

Property.h

Implementation of the property classes from Device Server Example.
See also:
Device.h

DeviceServer.h

#ifndef _EXAMPLES_PROPERTY_H_
#define _EXAMPLES_PROPERTY_H_

#include <stdlib.h>
#include <unistd.h>
#include <math.h>
#include <sys/time.h>
#include <sys/times.h>
#include <rda/DeviceServerBase.h>

//
// Data entry tags for values and timestamps
//
static const char* VTAG = "value";
static const char* TTAG = "timestamp";

//
// Returns current time as double in ms.
//
static double timeNow()
{
   struct timeval tv;
   struct timezone tz;
   gettimeofday(&tv, &tz);
   return 1000.0 * (double)tv.tv_sec + (double)(tv.tv_usec) / 1000.0;
}

//
// Logs the error message and terminates the server.
// Called when an rdaInternalError is caught in a thread other then main.
//
static void fatalError(const rdaInternalError& ex)
{
   char message[1000];
   sprintf(message, "RDA internal error [%s:%d]: %s",
           ex.getFile(), ex.getLine(), ex.getMessage());
   rdaDeviceServerBase::getGlobalLogger()->error(message);
   rdaDeviceServerBase::shutDown();
}

//
// The base class for properties of the example device.
//
class Property
{
   //
   // The property name
   //
   const char* name;

   protected:

      //
      // The property value and timestamp
      //
      double value;
      double timestamp;
      //
      // The value range
      //
      static const double minValue = 0;
      static const double maxValue = 100.0;

      //
      // Listeners attached to this property value
      //
      rdaValueChangeListener** listeners;
      int numListeners;

      //
      // Notify all clients that subscribed to the property value on
      // the value update.
      //
      void notifyClients(double newValue, bool hasChanged)
      {
         rdaData oldData;
         oldData.insert(VTAG, value);
         rdaData newData;
         newData.insert(VTAG, newValue);
         newData.insert(TTAG, timestamp);
         try
         {
            for (int i = 0; i < numListeners; i++)
               listeners[i]->valueUpdated(oldData, newData, hasChanged);
         }
         catch(const rdaInternalError& ex)
         {
               fatalError(ex);
         }
         value = newValue;
      }

   public:
 
      //
      // Constructor and destructor
      //
      Property(const char* pname)
      : name(pname), value(0), listeners(0), numListeners(0)
      {
         timestamp = timeNow();
      }

      virtual ~Property() {}

      //
      // Returns the property name (used in Device::findProperty())
      //
      const char* getName()
      {
         return name;
      }

      //
      // Returns the property value (used in Device::update())
      //
      double getValue()
      {
         return value;
      }

      //
      // Adds the specified object to the list of listeners to
      // this property value (called from Device::monitorOn()).
      //
      void addListener(rdaValueChangeListener* listener)
      {
         //
         // Allocate a new array large enough to hold the new
         // element, copy current contents to the new array,
         // and append the new element to the end of the new
         // array.
         //
         rdaValueChangeListener** newListeners =
            new rdaValueChangeListener* [numListeners + 1];
         for (int i=0; i < numListeners; i++)
            newListeners[i] = listeners[i];
         delete [] listeners;
         listeners = newListeners;
         listeners[numListeners] = listener;
         numListeners++;
         
         //
         // Immediately send current value to the new subscriber
         //
         rdaData* data = get();
         rdaData empty;
         try
         {
            listener->valueUpdated(empty, *data, true);
         }
         catch(const rdaInternalError& ex)
         {
            fatalError(ex);
         }
         delete data;
      }

      //
      // Removes the specified object from the list of listeners
      // to this property value (called from Device::monitorOff()).
      //
      void removeListener(rdaValueChangeListener* listener)
      {
         //
         // Allocate a new smaller array and copy all elements,
         // except the one to be removed, to the new array.
         //
         rdaValueChangeListener** newListeners =
            new rdaValueChangeListener* [numListeners - 1];
         int j = 0;
         for (int i=0; i < numListeners; i++)
            if (listeners[i] != listener) newListeners[j++] = listeners[i];
         delete [] listeners;
         listeners = newListeners;
         numListeners--;
      }

      //
      // Returns this property value and timestamp in a Data object
      // (called from Device::get()).
      //
      virtual rdaData* get()
      {
         rdaData* result = new rdaData;
         result->insert(VTAG, value);
         result->insert(TTAG, timestamp);
         return result;
      }

      //
      // Called from Device::set(), implemented in subclasses of Property.
      //
      virtual void set(const rdaData&) = 0;
};

//
// The measurement property of the example device
//
class Measurement : public Property
{
   public:

      Measurement() : Property("voltage") {}
      virtual ~Measurement() {}

      //
      // Updates the simulated measurement value (called from 
      // Device::update()).
      //
      void update(double newValue)
      {
         //
         // Force the value to stay within the limits
         //
         if (newValue < minValue) newValue = minValue;
         if (newValue > maxValue) newValue = maxValue;
         //
         // Update timestamp
         //
         timestamp = timeNow();
         //
         // Check if the value has changed and notify clients on the
         // value update.
         //
         bool hasChanged = (fabs(value - newValue) > 1.0); // deadband = 1.0
         notifyClients(newValue, hasChanged);
      }

      //
      // Implements the abstract set method of the base class
      //
      virtual void set(const rdaData&)
      {
         throw rdaIOError("EXAMPLE", 1, "Can't set readonly property");
      }
};

//
// The setting property of the example device
//
class Setting : public Property
{
   public:

      Setting() : Property("requestedVoltage") {}
      virtual ~Setting() {}

      //
      // Implements the abstract set method of the base class
      //
      virtual void set(const rdaData& data)
      {
         double newValue = 0;
         //
         // Check if the data object contains a value of the type
         // double. If not, throw exceptions: the exceptions will
         // propagate directly to the client.
         //
         try
         {
            newValue = data.extractDouble(VTAG);
         }
         catch(const rdaBadParameter&)
         {
            throw rdaBadParameter("No 'value' entry in value data");
         }
         catch(const rdaTypeMismatch&)
         {
            throw rdaBadParameter("Bad value type: 'double' expected");
         }
         //
         // Check if the requested value is within the legal range
         //
         if (newValue < minValue || newValue > maxValue)
            throw rdaBadParameter("Value out of range");
         //
         // Set timestamp and notify clients on the value update.
         //
         timestamp = timeNow();
         notifyClients(newValue, true);
      }
};

#endif

RDA-2.3 documentation - 27 Jun 2007 - N.Trofimov