#include <cdevMonitorTable.h>

// *****************************************************************************
// * cdevMonitorNode::cdevMonitorEntry Static Variables.
// *****************************************************************************
int cdevMonitorNode::cdevMonitorEntry::VALUE_TAG  = 0;
int cdevMonitorNode::cdevMonitorEntry::STATUS_TAG = 0;
int cdevMonitorNode::cdevMonitorEntry::TIME_TAG   = 0;
int cdevMonitorNode::cdevMonitorEntry::DEVICE_TAG = 0;


// *****************************************************************************
// * cdevMonitorData::cdevMonitorData:
// *	This is the default constructor.
// *****************************************************************************
cdevMonitorData::cdevMonitorData ( void ) :
	criticalTags(NULL), criticalTagCnt(0), cdevData()
	{
	}

// *****************************************************************************
// * cdevMonitorData::cdevMonitorData:
// *	This builds a cdevMonitorData from a cdevData.
// *****************************************************************************
cdevMonitorData::cdevMonitorData ( const cdevData & data ) :
	criticalTags(NULL), criticalTagCnt(0), cdevData(data)
	{
	}

// *****************************************************************************
// * cdevMonitorData::cdevMonitorData:
// *	This builds a cdevMonitorData from a cdevMonitorData.
// *****************************************************************************
cdevMonitorData::cdevMonitorData ( const cdevMonitorData & data ) :
	criticalTags(data.criticalTags),
	criticalTagCnt(data.criticalTagCnt), 
	cdevData(*(const cdevData *)&data)
	{
	}

// *****************************************************************************
// * isTagCritical :
// *	This method will walk through the list of tags and attempt to find
// *	the caller specified tag.  This method returns 1 if it is present, or
// *	0 if it is not.
// *****************************************************************************
int cdevMonitorData::isTagCritical ( int tag )
	{
	for(int i=0; i<criticalTagCnt && tag!=criticalTags[i]; i++);
	return i<criticalTagCnt?1:0;
	}

// *****************************************************************************
// * setCriticalTags :
// *	This method is used to specify the tags in the cdevData object that
// *	are critical.
// *****************************************************************************
void cdevMonitorData::setCriticalTags ( int * tags, int tagCnt )
	{
	criticalTags   = tags;
	criticalTagCnt = tagCnt;
	}
	
// *****************************************************************************
// * changeTag :
// *	This method is called when the tags in the cdevData object are remapped
// *	to the client tags.  When the tag is remapped in this method, the
// *	critical tag value (if it exists) will also be remapped.
// *****************************************************************************
int cdevMonitorData::changeTag ( int oldTag, int newTag )
	{
	for(int i=0; i<criticalTagCnt && criticalTags[i]!=oldTag; i++);
	if(criticalTags[i]==oldTag) criticalTags[i] = newTag;
	return cdevData::changeTag(oldTag, newTag);
	}

// *****************************************************************************
// * xdrSize:
// *	Calculates the size of the XDR Buffer necessary to store the cdevData 
// *	object.  This function may be used to determine the size of a 
// *	preallocated buffer for storing the xdr representation of a cdevData
// *	object.  The bufLen parameter will receive the size of the buffer, and
// *	the elementCount variable will receive the number of elements that will
// * 	be stored in the buffer.
// *****************************************************************************
int cdevMonitorData::xdrSize  (size_t * bufLen, size_t * elementCount)
	{
	cdevDataEntry * ptr            = entries;
	int             xdrDataSize    = 0;
	int             cdevElementCnt = 0;
	int             i;
	
	// ************************************#
	// * CALCULATE THE SIZE OF THE BUFFER	#
	// ************************************#
	// *************************************
	// * Add the size of the tag count int *
	// *************************************
	xdrDataSize += XDR_Sizeof(cdevElementCnt);

	// *************************************
	// * Add the size of each valid item	 *
	// *************************************
	for(ptr=entries; ptr!=NULL; ptr = ptr->next_)
		{ 
		// *****************************
		// * Do not process entries    *
		// * with CDEV_INVALID as the  *
		// * dataType or 0 as the tag. *
		// *****************************
		if(ptr->dataType_==CDEV_INVALID || 
		   ptr->tag_==0                 ||
		   !isTagCritical(ptr->tag_)) continue;
		
		// *****************************
		// * Calculate the number of   *
		// * elements in this tagged   *
		// * data item.	               *
		// *****************************
		int numElements = ptr->dim_!=0?ptr->elems_:1;
		
		// *****************************
		// * Increment the counter     *
		// ***************************** 
		cdevElementCnt++;
		
		// *****************************
		// * Add the size of the       *
		// * cdevDataEntries tag_,     *
		// * dataType_, dim_, elems_.  * 
		// *****************************
		xdrDataSize += 4 * XDR_Sizeof((int)1);
		
		// *****************************
		// * Add the size of the       *
		// * cdevBounds data.          *
		// *****************************
		xdrDataSize += ptr->dim_ * (XDR_Sizeof((int)1) * 2);
		
		// *****************************
		// * Add the size of the data  *
		// *****************************
		if     (ptr->dataType_==CDEV_BYTE)   xdrDataSize += numElements * XDR_Sizeof((unsigned char)'c');
		else if(ptr->dataType_==CDEV_INT16)  xdrDataSize += numElements * XDR_Sizeof((short)1);
		else if(ptr->dataType_==CDEV_UINT16) xdrDataSize += numElements * XDR_Sizeof((unsigned short)1);
		else if(ptr->dataType_==CDEV_INT32)  xdrDataSize += numElements * XDR_Sizeof((long)1);
		else if(ptr->dataType_==CDEV_UINT32) xdrDataSize += numElements * XDR_Sizeof((unsigned long)1);
		else if(ptr->dataType_==CDEV_FLOAT)  xdrDataSize += numElements * XDR_Sizeof((float)1);
		else if(ptr->dataType_==CDEV_DOUBLE) xdrDataSize += numElements * XDR_Sizeof((double)1);
		else if(ptr->dataType_==CDEV_STRING)
			{
			if(numElements==1) xdrDataSize += XDR_Sizeof(ptr->data_.str);
			else for(i=0; i<numElements; i++) xdrDataSize += XDR_Sizeof(ptr->data_.strarr[i]);
			}
		else if(ptr->dataType_==CDEV_TIMESTAMP) xdrDataSize += numElements * XDR_Sizeof(ptr->data_.ts);
		}
	
	*elementCount = cdevElementCnt;
	*bufLen       = xdrDataSize;
	
	return *bufLen>0?CDEV_SUCCESS:CDEV_ERROR;
	}


// *****************************************************************************
// * xdrExport:
// *	This function encapsulates the contents of the cdevData class into a 
// *	binary stream.  This function allocates the buffer and returns the new 
// *	buffer and the buffer size.
// *****************************************************************************
int cdevMonitorData::xdrExport ( char ** buf, size_t * bufLen )
{	
  size_t count = 0;
  
  // ************************************#
  // * Calculate the size of the buffer  #
  // ************************************#
  xdrSize(bufLen, &count);
  
  // ************************************#
  // * Allocate the buffer and call the  #
  // * export function.                  #
  // ************************************#
  if((*buf = new char[*bufLen])!=NULL) xdrExport(*buf, *bufLen, count);
  
  return buf==NULL?CDEV_ERROR:CDEV_SUCCESS;
}


// *****************************************************************************
// * xdrExport:
// *	This function encapsulates the contents of the cdevData class into a 
// *	preallocated binary stream.  The buf parameter contains the address of 
// *	the caller allocated buffer, the bufLen parameter specifies the size
// *	of the buffer, and the count parameter specifies the number of tagged 
// *	data items that will be copied from the cdevData object into the
// *	binary stream.
// *****************************************************************************
int cdevMonitorData::xdrExport ( char * buf, size_t bufLen, size_t count )
	{
	cdevDataEntry * ptr;
	XDR_Writer      writer;
	int             xdrDataSize    = bufLen;
	int             cdevElementCnt = count;
	int             i; 
	
	// ************************************#
	// * Allocate the buffer               #
	// ************************************#
	writer.attachData(buf, bufLen);
	
	// ************************************#
	// * Transfer the data to the buffer   #
	// ************************************#
	// *************************************
	// * Write the number of elements      *
	// *************************************
	writer.put(cdevElementCnt);
	
	// *************************************
	// * Write each valid item.            *
	// *************************************
	for(ptr = entries; ptr != NULL; ptr = ptr->next_)
		{
		// *****************************
		// * Do not process entries    *
		// * with CDEV_INVALID as the  *
		// * dataType or 0 as the tag. *
		// *****************************
		if(ptr->dataType_==CDEV_INVALID || 
		   ptr->tag_==0                 ||
		   !isTagCritical(ptr->tag_)) continue;
		
		// *****************************
		// * Calculate the number of   *
		// * elements in this tagged   *
		// * data item.                *
		// *****************************
		int numElements = ptr->dim_!=0?ptr->elems_:1;
		
		// *****************************
		// * Decrement the counter     *
		// ***************************** 
		cdevElementCnt--;
		
		// *****************************
		// * Write the tag_, dataType_ *
		// * dim_, and elems_ of the   *
		// * cdevDataEntry object.     * 
		// *****************************
		writer.put((int)ptr->tag_);
		writer.put((int)ptr->dataType_);	
		writer.put((int)ptr->dim_);
		writer.put((int)ptr->elems_);
		
		// *****************************
		// * Write the cdevBounds      *
		// * values asssociated with   *
		// * the cdevDataEntry (if any)*
		// *****************************
		cdevBounds * bounds = ptr->bounds();
		for(i=0; i<ptr->dim_; i++) 
			{
			writer.put((int)bounds[i].offset);
			writer.put((int)bounds[i].length);
			}
			
		// *****************************
		// * Write the data            *
		// *****************************
		if(ptr->dataType_==CDEV_BYTE)
			{
			if(numElements==1) writer.put(ptr->data_.cval);
			else for(i=0; i<numElements; i++) writer.put(ptr->data_.cptr[i]);
			}
		else if(ptr->dataType_==CDEV_INT16)       
			{
			if(numElements==1) writer.put(ptr->data_.sval);
			else for(i=0; i<numElements; i++) writer.put(ptr->data_.sptr[i]);
			}
		else if(ptr->dataType_==CDEV_UINT16)       
			{
			if(numElements==1) writer.put(ptr->data_.usval);
			else for(i=0; i<numElements; i++) writer.put(ptr->data_.usptr[i]);
			}
		else if(ptr->dataType_==CDEV_INT32)       
			{
			if(numElements==1) writer.put(ptr->data_.lval);
			else for(i=0; i<numElements; i++) writer.put(ptr->data_.lptr[i]);
			}
		else if(ptr->dataType_==CDEV_UINT32)       
			{
			if(numElements==1) writer.put(ptr->data_.ulval);
			else for(i=0; i<numElements; i++) writer.put(ptr->data_.ulptr[i]);
			}
		else if(ptr->dataType_==CDEV_FLOAT)
			{
			if(numElements==1) writer.put(ptr->data_.fval);
			else for(i=0; i<numElements; i++) writer.put(ptr->data_.fptr[i]);
			}
		else if(ptr->dataType_==CDEV_DOUBLE)
			{
			if(numElements==1) writer.put(ptr->data_.dval);
			else for(i=0; i<numElements; i++) writer.put(ptr->data_.dptr[i]);
			}
		else if(ptr->dataType_==CDEV_STRING) 
			{
			if(numElements==1) writer.put(ptr->data_.str);
			else for(i=0; i<numElements; i++) writer.put(ptr->data_.strarr[i]);
			}
		else if(ptr->dataType_==CDEV_TIMESTAMP) 
			{
			if(numElements==1) writer.put(ptr->data_.ts);
			}
		}
	
	// *************************************
	// * Detach the data from the writer   *
	// * object before the object is       *
	// * destroyed.                        *
	// *************************************
	writer.detachData();
	
	return CDEV_SUCCESS;
	}
	

// *****************************************************************************
// * cdevMonitorNode::cdevMonitorEntry::cdevMonitorEntry :
// *	This is the constructor for the cdevMonitorEntry class that is internal to the
// *	cdevMonitorNode class.  
// *
// *	This constructor will initialize all data items and will then populate
// *	the list of triggers with all properties from the context that are
// *	marked with 2 or 3.  It will then populate the properties list with
// *	all properties from the context that have any value other than 0.
// *
// *	If not properties are in the context, then the following settings will
// *	be used by default...
// *		value  = 3
// *		status = 1
// *		time   = 1
// *
// *	The cdevMessage object becomes the property of the cdevMonitorNode
// *	object and should not be accessed again by the caller.
// *
// *	The deviceIdx indicates the specific device that is being monitored
// *	within a multi-device list.
// *****************************************************************************
cdevMonitorNode::cdevMonitorEntry::cdevMonitorEntry( cdevMessage * Message, int DeviceIdx=0 )
	: next(NULL), message(Message), triggers(NULL), trigType(NULL), 
	trigCnt(0), properties(NULL), propCnt(0), deviceIdx(DeviceIdx)
	{
	// *********************************************************************
	// * Populate the static variables if they have not already been set.
	// *********************************************************************
	if(VALUE_TAG==0)
		{
		cdevData::tagC2I("value",  &VALUE_TAG);
		cdevData::tagC2I("status", &STATUS_TAG);
		cdevData::tagC2I("time",   &TIME_TAG);

		// *************************************************************
		// * Added to support multi-device monitors.
		// *************************************************************
		cdevData::tagC2I("device", &DEVICE_TAG);
		}
		
	if(message!=NULL)
		{
		int              tag;
		size_t           tagElems;
		int              tagVal;

		// *************************************************************
		// * Start by removing the cdevData data if it has been 
		// * included.  This component will not be necessary to process
		// * the monitors.
		// *************************************************************
		message->setData(NULL);
		
		// *************************************************************
		// * If the context has been specified, then read the context
		// * to determine the number of monitorable data items.
		// *************************************************************
		if(message->getContext()!=NULL)
			{
			cdevData *       context = message->getContext();
			cdevDataIterator iter(context);
			iter.init();
			
			while((tag=iter.tag())!=0)
				{
				tagElems = 0;
				tagVal   = 0;
				context->getElems(tag, &tagElems);
				if(tagElems==1)
					{
					context->get(tag, &tagVal);
					if(tagVal>1) trigCnt++;
					if(tagVal>0) propCnt++;
					}
				++iter;
				}
			// *****************************************************
			// * If the number of monitorable data items is greater 
			// * than 0, then allocate a sufficient buffer and copy 
			// * the tag names and tag types into the buffer.
			// *****************************************************
			if(trigCnt>0)
				{
				// *********************************************
				// * Added to support multi-device list.
				// *********************************************
				if(message->getDeviceCount()>1) propCnt++;
				
				iter.init();
				triggers   = new int[trigCnt+trigCnt+propCnt];
				trigType   = &triggers[trigCnt];
				properties = &triggers[trigCnt+trigCnt];
				trigCnt    = 0;
				propCnt    = 0;
				while((tag=iter.tag())!=0) 
					{
					tagElems = 0;
					tagVal   = 0;
					context->getElems(tag, &tagElems);
					if(tagElems==1)
						{
						context->get(tag, &tagVal);
						if(tagVal>1)
							{
							triggers[trigCnt]   = tag;
							trigType[trigCnt++] = tagVal;
							}
						if(tagVal>0) properties[propCnt++] = tag;
						}
					++iter;
					}
				// *********************************************
				// * Added to support multi-device list.
				// *********************************************
				if(message->getDeviceCount()>1) 
					properties[propCnt++] = DEVICE_TAG;
				}
			}

		// *************************************************************
		// * If not tags were specified or the context was blank... then
		// * use the default setup.
		// *************************************************************
		if(trigCnt<=0)
			{
			trigCnt       = 1;
			propCnt       = 3;

			// *****************************************************
			// * Added to support multi-device list.
			// *****************************************************
			if(message->getDeviceCount()>1) propCnt++;
				
			triggers      = new int[2+propCnt];
			trigType      = &triggers[1];
			properties    = &triggers[2];
			triggers[0]   = VALUE_TAG;
			trigType[0]   = 3;
			properties[0] = VALUE_TAG;
			properties[1] = STATUS_TAG;
			properties[2] = TIME_TAG;
			// *****************************************************
			// * Added to support multi-device list.
			// *****************************************************
			if(message->getDeviceCount()>1) properties[3] = DEVICE_TAG;
			}
		}
	}
	
	
// *****************************************************************************
// * cdevMonitorNode::cdevMonitorEntry::~cdevMonitorEntry :
// *	This is the destructor for the node object that is internal to the 
// *	cdevMonitorNode class.  It is responsible for deleteing the cdevMessage
// *	object and freeing any memory that was allocated to the triggers
// *	object.
// *
// *	Note that this destructor will delete all of the nodes that are stored
// *	below this one in the list.
// ******************************************************************************
cdevMonitorNode::cdevMonitorEntry::~cdevMonitorEntry ( void )
	{
	if(message)    delete message;
	if(triggers)   delete triggers;
	if(next!=NULL) delete next;
	}
	

// *****************************************************************************
// * cdevMonitorNode::cdevMonitorNode :
// *	This is the constructor for the cdevMonitorNode class.  It will save a
// *	copy of the Parent variable and will use the user specified Device and
// *	Attrib to create the hashString.
// *****************************************************************************
cdevMonitorNode::cdevMonitorNode ( cdevMonitorTable * Parent, 
                                   char             * Device,
                                   char             * Attrib )
	: parent(Parent), hashString(NULL), nodes(NULL)
	{
	hashString = new char[strlen(Device)+strlen(Attrib)+2];
	sprintf(hashString, "%s %s", Device, Attrib);
	}


// *****************************************************************************
// * cdevMonitorNode::~cdevMonitorNode :
// *	This is the destructor for the cdevMonitorNode object.  It will delete
// *	the hashString and each node from the list of monitors. 
// *****************************************************************************
cdevMonitorNode::~cdevMonitorNode ( void )
	{
	if(hashString) delete hashString;
	if(nodes)
		{
		// *************************************************************
		// * Prior to deleteing itself, this class will first fire its
		// * monitors once more using a 1 in the endOfTransaction 
		// * parameter in order to inform the client side that it will 
		// * receive no further transactions.
		// *************************************************************
		cdevMonitorEntry * node = nodes;
		while(node) 
			{ 
			fireMonitor(node, -1, &mData, 1);
			node = node->next;
			}
		delete nodes;
		}
	}
	
// *****************************************************************************
// * cdevMonitorNode::insertMonitor :
// *	This method is used to add a new monitor for the device / attribute
// *	pair associated with this object.  
// *
// *	The cdevData object that is provided in the cdevData object is the 
// *	collection of all current property values for the device / attribute 
// *	pair. This data will be used to automatically dispatch the first
// *	callback when the monitor is initially installed.
// *
// *	The deviceIdx indicates the specific device that is being monitored
// *	within a multi-device list.
// *****************************************************************************
int cdevMonitorNode::insertMonitor ( cdevMessage * request, 
                                     cdevData    * data, 
                                     int           deviceIdx = 0 )
	{
	int    result = 0;
	cdevMonitorEntry * node = new cdevMonitorEntry(request, deviceIdx);
	if(node->trigCnt > 0)
		{
		node->next = nodes;
		nodes      = node;
		}
	else	{
		delete node;
		result = -1;
		}
	if(data!=NULL)
		{
		mData = *data;
		fireMonitor(node, -1, &mData);
		}
	return result;
	}
	
// *****************************************************************************
// * cdevMonitorNode::removeMonitor :
// *	This method is used to remove a specific monitor by submitting a 
// *	cdevMessage that contains the transaction index to be canceled in the
// *	cancelTransIndex element.
// *****************************************************************************
int cdevMonitorNode::removeMonitor ( cdevMessage * request )
	{
	return request==NULL?-1:removeMonitor(request->getCancelTransIndex());
	}	

// *****************************************************************************
// * cdevMonitorNode::removeMonitor :
// *	This method is used to remove a specific monitor by submitting a 
// *	the transaction index to be canceled.
// *****************************************************************************
int cdevMonitorNode::removeMonitor ( unsigned cancelTransIndex )
	{
	int    result = -1;
	cdevMonitorEntry * node = nodes, *prev = NULL;
	
	while(node!=NULL && 
	      node->message->getTransIndex() != cancelTransIndex)
		{
		prev = node;
		node = node->next;
		}
	if(node!=NULL)
		{
		if(prev==NULL) nodes      = node->next;
		else           prev->next = node->next;
		node->next = NULL;
		
		// *************************************************************
		// * This code portion added to inform client side that the
		// * monitor is terminating.  When a monitor returns an 
		// * operationCode of 0 - it indicates that there will be no
		// * further data transmissions.
		// *
		// * The 1 provided in the endOfTransaction parameter indicates
		// * that this transaction is finished.
		// *************************************************************
		fireMonitor(node, -1, &mData, 1);

		delete node;
		result = 0;
		}
	return result;
	}	


// *****************************************************************************
// * cdevMonitorNode::removeClientMonitors :
// *	This method is used to remove all nodes that are associated with a 
// *	specified clientID.
// *****************************************************************************
int cdevMonitorNode::removeClientMonitors ( short clientID, int fire )
	{
	cdevMonitorEntry * node = nodes, *prev = NULL;
	while(node!=NULL)
		{
		if(node->message->getClientID() == clientID)
			{
			cdevMonitorEntry * nextcdevMonitorEntry = node->next;
			node->next = NULL;
	
			// *****************************************************
			// * This code portion added to inform client side that 
			// * the monitor is terminating.  When a monitor returns 
			// * an operationCode of 0 - it indicates that there 
			// * will be no further data transmissions.
			// *
			// * The 1 provided in the endOfTransaction parameter 
			// * indicates that this transaction is finished.
			// *****************************************************
			if(fire) fireMonitor(node, -1, &mData, 1);
	
			delete node;
			
			if(prev==NULL) nodes      = nextcdevMonitorEntry;
			else           prev->next = nextcdevMonitorEntry;
			node = nextcdevMonitorEntry;
			}
		else	{
			prev = node;
			node = node->next;
			}
		}
	return 0;
	}
	
// *****************************************************************************
// * cdevMonitorNode::fireMonitor :
// *	This method is used to submit a change to a monitored property.  If the
// *	value of property is NULL, then all of the trigger properties will be
// *	fired.
// *
// *	Note that the data parameter should be populated with all of the 
// *	properties that are associated with the particular device / attribute
// *	pair.
// *****************************************************************************
int cdevMonitorNode::fireMonitor( char * property, cdevData * data )
	{
	int tag = -1;
	if(property!=NULL) cdevData::tagC2I(property, &tag);
	return fireMonitor(tag, data);
	}


// *****************************************************************************
// * cdevMonitorNode::fireMonitor :
// *	This method is used to submit a change to a monitored property.  If the
// *	value of property is -1, then it will fire all monitors that are listed
// *	in the triggers parameter.
// *
// *	Note that the data parameter should be populated with all of the 
// *	properties that are associated with the particular device / attribute
// *	pair.
// *
// *	This method will return 0 if any monitors were actually dispatched, 
// *	otherwise it will return -1.
// *****************************************************************************
int cdevMonitorNode::fireMonitor( int property, cdevData * data )
	{
	int                result = -1;
	cdevMonitorEntry * node = nodes;

	// *********************************************************************
	// * Attach the data that is contained in the cdevData object to the
	// * cdevMonitorData object.
	// *********************************************************************
	if(data!=NULL)
		{
		// *************************************************************
		// * Note here I am copying the contents of the user provided
		// * data to the mData structure that is local to the 
		// * cdevMonitorNode.
		// *************************************************************
		mData = *data;
		while(node!=NULL)
			{
			if(fireMonitor(node, property, &mData)==0) result = 0;
			node = node->next;
			}
		}
	return result;	
	}

// *****************************************************************************
// * cdevMonitorNode::fireMonitor :
// *	This method is used to submit a change to a monitored property.  If the
// *	value of property is -1, then it will fire all monitors that are listed
// *	in the triggers parameter.
// *
// *	The endOfTransaction flag indicates that this is the last monitor that
// *	will be returned in association with this cdevMonitorNode and, 
// *	therefore, the cdevMessage's operationCode should be set to indicate 
// *	that.
// *
// *	Note that the data parameter should be populated with all of the 
// *	properties that are associated with the particular device / attribute
// *	pair.
// *
// *	This method will return 0 if any monitors were actually dispatched, 
// *	otherwise it will return -1.
// *****************************************************************************
int cdevMonitorNode::fireMonitor( cdevMonitorEntry * entry, 
                                  int property, 
                                  cdevMonitorData *data,
                                  int endOfTransaction )
	{
	int result = -1;

	if(entry!=NULL && data!=NULL)
		{
		int found = 0;

		// *************************************************************
		// * An operationCode value with bit one set indicates that this
		// * is NOT the end of the transaction, an operationCode value 
		// * with bit one unset indicates that this IS the end of the
		// * transaction.
		// *************************************************************
		entry->message->setOperationCode(endOfTransaction?0:1);

		for(int i=0; !found && i<entry->trigCnt; i++)
			{
			if(property<0 || entry->triggers[i]==property)
				{
				if(property>0) found = 1;
				// *********************************************
				// * Trigger type 3 means that the callback 
				// * wants the changed value and all other
				// * values that were specified in the
				// * context.
				// *********************************************
				if(entry->trigType[i]==3)
					data->setCriticalTags(entry->properties, 
					                     entry->propCnt);
				
				// *********************************************
				// * Trigger type 2 means that the callback
				// * wants only the changed value.
				// *********************************************
				if(entry->trigType[i]==2)
					data->setCriticalTags(&entry->triggers[i], 1);
				
				// *********************************************
				// * Determine if the changed data item exists
				// * in the user provided data.  If it does, 
				// * then execute the fireCallback method on 
				// * the cdevMonitorTable to dispatch the
				// * monitor.
				// *********************************************
				if(data->getType(entry->triggers[i])!=CDEV_INVALID)
					{
					// *************************************
					// * Added to support multi-device
					// *************************************
					if(entry->message->getDeviceCount()>1)
						data->insert(cdevMonitorEntry::DEVICE_TAG, 
						             entry->message->getDeviceList()
						             	[entry->deviceIdx]);

					entry->message->setData(data, 1);
					parent->fireCallback(entry->message);

					// *************************************
					// * Added to support multi-device
					// *************************************
					if(entry->message->getDeviceCount()>0)
						data->remove(cdevMonitorEntry::DEVICE_TAG);

					result = 0;
					}
				}
			}
		}
	
	return result;	
	}


// *****************************************************************************
// * cdevMonitorTable::cdevMonitorTable :
// *	This is the constructor for the cdevMonitortable class.  This class
// *	contains a hash table that maintains a list of all of the monitored
// *	device / attribute pairs on the system.
// *****************************************************************************
cdevMonitorTable::cdevMonitorTable ( void )
	: monitors(0)
	{
	}
	
// *****************************************************************************
// * cdevMonitorTable::~cdevMonitorTable :
// *	This method walks through the monitors hash table and deletes all
// *	cdevMonitorNodes that have been allocated.
// *****************************************************************************
cdevMonitorTable::~cdevMonitorTable ( void )
	{
	char * ptr;
	StringHashIterator iter(&monitors);
	
	iter.first();
	while((ptr= iter.key())!=NULL)
		{
		cdevMonitorNode * node = (cdevMonitorNode *)iter.data();
		monitors.remove(ptr);	
		delete node;
		iter.first();
		}
	}
	
// *****************************************************************************
// * cdevMonitorTable::insertMonitor:
// *	This method allows the caller to add a new monitored device / attribute
// *	pair to the cdevMonitorTable.
// *
// *	The cdevData object that is provided in the cdevData object is the 
// *	collection of all current property values for the device / attribute 
// *	pair. This data will be used to automatically dispatch the first
// *	callback when the monitor is initially installed.
// *****************************************************************************
int cdevMonitorTable::insertMonitor ( cdevMessage * request, cdevData * data )
	{
	return insertMonitor(request, &data, 1);
	}
	

// *****************************************************************************
// * cdevMonitorTable::insertMonitor:
// *	This method allows the caller to add a new monitored device / attribute
// *	pair to the cdevMonitorTable.
// *
// *	The cdevData objects that are provided in the data parameter are the 
// *	initial values for each item in the deviceList in sequence.  This data 
// *	will be used to automatically dispatch the first callback when the 
// *	monitor is initially installed.
// *****************************************************************************
int cdevMonitorTable::insertMonitor ( cdevMessage * request, cdevData ** data, size_t dataCnt )
	{
	if(request!=NULL && request->getDeviceList()!=NULL && request->getMessage()!=NULL)
		{
		char * attrib = strchr(request->getMessage(), ' ');
		if(attrib)
			{
			attrib++;
			cdevMonitorNode *  node;
			cdevMessage     ** msgList;
			
			// *****************************************************
			// * Allocate additional cdevMessage objects if a 
			// * deviceList greater than 1 is specified.
			// *****************************************************
			if(request->getDeviceCount()>1)
				{
				msgList    = new cdevMessage *[request->getDeviceCount()];
				msgList[0] = request;
				}
			else msgList = &request;
			 
			// *****************************************************
			// * Walk through each device in the list.
			// *****************************************************
			for(int i=0; i<request->getDeviceCount(); i++)
				{
				if(i>0) msgList[i] = new cdevMessage(*request);
				if((node=findMonitor(request->getDeviceList()[i], attrib))==NULL) 
					{
					node = new cdevMonitorNode(this, request->getDeviceList()[i], attrib);
					monitors.insert(node->getHashString(), node);
					}
				if(dataCnt>0 && data!=NULL)
					{
					node->insertMonitor(msgList[i], ((i<dataCnt)?data[i]:data[dataCnt-1]), i);
					}
				else    node->insertMonitor(msgList[i], NULL, i);
				}
			
			// *****************************************************
			// * Delete the containing array of cdevMessage pointers
			// *****************************************************
			if(request->getDeviceCount()>1) delete msgList;
			}
		}
	return 0;
	}
	

// *****************************************************************************
// * cdevMonitorTable::removeMonitor :
// *	This method allows the caller to remove a specific monitor using the 
// *	cancelTransIndex component of the cdevMessage object.
// *****************************************************************************
int cdevMonitorTable::removeMonitor ( cdevMessage * request )
	{
	int cancelTransIndex = request->getCancelTransIndex();
	if(cancelTransIndex > 0)
		{ 
		StringHashIterator iter(&monitors);	
		iter.first();
	
		while(iter.key()!=NULL)
			{
			cdevMonitorNode * node = (cdevMonitorNode *)iter.data();
			node->removeMonitor(cancelTransIndex);
			++iter;
			}
		}
	return 0;
	}
	
// *****************************************************************************
// * cdevMonitorTable::removeClientMonitors :
// *	This method will walk through the hash table and will remove all
// *	monitors that are associated with a specific clientID.
// *****************************************************************************
int cdevMonitorTable::removeClientMonitors ( short clientID, int fire )
	{
	char * ptr;
	StringHashIterator iter(&monitors);
	
	iter.first();
	while((ptr= iter.key())!=NULL)
		{
		cdevMonitorNode * node = (cdevMonitorNode *)iter.data();
		node->removeClientMonitors(clientID, fire);
		iter++;
		}
	return 0;
	}
	
// *****************************************************************************
// * cdevMonitorTable::findMonitor :
// *	This method locates a cdevMonitorNode associated with the specified
// *	device / attribute pair and returns it to the caller.
// *****************************************************************************
cdevMonitorNode * cdevMonitorTable::findMonitor ( char * device, char * attrib )
	{
	char hashString [512];
	sprintf(hashString, "%s %s", device, attrib);
	return (cdevMonitorNode *)monitors.find(hashString);
	}
	
	
// *****************************************************************************
// * cdevMonitorTable:fireMonitor :
// *	This method is used to deploy all monitors associated with the 
// *	device / attribute / property set.
// *
// *	If the property parameter is NULL, then all properties will be fired 
// *	for all monitor associated with the device / attribute pair.
// *****************************************************************************
int cdevMonitorTable::fireMonitor ( char * device, char * attrib, 
                                    char * property, cdevData * data )
	{
	cdevMonitorNode * node = findMonitor(device, attrib);
	return node==NULL?-1:node->fireMonitor(property, data);
	}

// *****************************************************************************
// * cdevMonitorTable:fireMonitor :
// *	This method is used to deploy all monitors associated with the 
// *	device / attribute / property set.
// *
// *	If the property parameter is -1, then all properties will be fired 
// *	for all monitors associated with the device / attribute pair.
// *****************************************************************************
int cdevMonitorTable::fireMonitor ( char * device, char * attrib, 
                                    int property, cdevData * data )
	{
	cdevMonitorNode * node = findMonitor(device, attrib);
	return node==NULL?-1:node->fireMonitor(property, data);
	}


// *****************************************************************************
// * cdevMonitorTable::fireCallback :
// *	This is a stub for the fireCallback method that must b e provided by
// *	the developer.  This method is provided here - because this method may 
// *	potentially be called during program termination - resulting in the 
// *	attempted execution of a pure virtual function.
// *****************************************************************************
int cdevMonitorTable::fireCallback ( cdevMessage * /* message */ )
	{
	return CDEV_SUCCESS;
	}
