#include <VirtualAttrib.h>

static int VALUE_TAG_ID       = -1;
static int STATUS_TAG_ID      = -1;
static int SEVERITY_TAG_ID    = -1;
static int UNITS_TAG_ID       = -1;
static int ALARMHIGH_TAG_ID   = -1;
static int ALARMLOW_TAG_ID    = -1;
static int WARNINGHIGH_TAG_ID = -1;
static int WARNINGLOW_TAG_ID  = -1;
static int CONTROLHIGH_TAG_ID = -1;
static int CONTROLLOW_TAG_ID  = -1;

#define VALUE_TAG       ((VALUE_TAG_ID<0 && cdevData::tagC2I("value", &VALUE_TAG_ID)!=CDEV_SUCCESS)?-1:VALUE_TAG_ID)
#define STATUS_TAG      ((STATUS_TAG_ID<0 && cdevData::tagC2I("status", &STATUS_TAG_ID)!=CDEV_SUCCESS)?-1:STATUS_TAG_ID)
#define SEVERITY_TAG    ((SEVERITY_TAG_ID<0 && cdevData::tagC2I("severity", &SEVERITY_TAG_ID)!=CDEV_SUCCESS)?-1:SEVERITY_TAG_ID)
#define UNITS_TAG       ((UNITS_TAG_ID<0 && cdevData::tagC2I("units", &UNITS_TAG_ID)!=CDEV_SUCCESS)?-1:UNITS_TAG_ID)
#define ALARMHIGH_TAG   ((ALARMHIGH_TAG_ID<0 && cdevData::tagC2I("alarmHigh", &ALARMHIGH_TAG_ID)!=CDEV_SUCCESS)?-1:ALARMHIGH_TAG_ID)
#define ALARMLOW_TAG    ((ALARMLOW_TAG_ID<0 && cdevData::tagC2I("alarmLow", &ALARMLOW_TAG_ID)!=CDEV_SUCCESS)?-1:ALARMLOW_TAG_ID)
#define WARNINGHIGH_TAG ((WARNINGHIGH_TAG_ID<0 && cdevData::tagC2I("warningHigh", &WARNINGHIGH_TAG_ID)!=CDEV_SUCCESS)?-1:WARNINGHIGH_TAG_ID)
#define WARNINGLOW_TAG  ((WARNINGLOW_TAG_ID<0 && cdevData::tagC2I("warningLow", &WARNINGLOW_TAG_ID)!=CDEV_SUCCESS)?-1:WARNINGLOW_TAG_ID)
#define CONTROLHIGH_TAG ((CONTROLHIGH_TAG_ID<0 && cdevData::tagC2I("controlHigh", &CONTROLHIGH_TAG_ID)!=CDEV_SUCCESS)?-1:CONTROLHIGH_TAG_ID)
#define CONTROLLOW_TAG  ((CONTROLLOW_TAG_ID<0 && cdevData::tagC2I("controlLow", &CONTROLLOW_TAG_ID)!=CDEV_SUCCESS)?-1:CONTROLLOW_TAG_ID)

// *****************************************************************************
// * VirtualAttrib::VirtualAttrib :
// *	This is the constructor for the VirtualAttrib class.  It initializes the
// *	internal mechanisms to 0.
// *****************************************************************************
VirtualAttrib::VirtualAttrib ( char * Device, char * Attrib )
	: device(strdup(Device)), 
	  attrib(strdup(Attrib)),
	  monitors(NULL), 
	  value(0.0), 
	  alarmHigh(0.0),   
	  alarmLow(0.0),
	  warningHigh(0.0), 
	  warningLow(0.0),
	  controlHigh(0.0), 
	  controlLow(0.0)
	{
	*severity = 0;
	*units    = 0;
	strcpy(status, "NORMAL");
	}
	

// *****************************************************************************
// * VirtualAttrib::~VirtualAttrib :
// *	This is the destructor for the VirtualAttrib class.  It must free the
// *	memory associated with the device and attribute names.
// *****************************************************************************
VirtualAttrib::~VirtualAttrib ( void )
	{
	delete device;
	delete attrib;
	}
	
// *****************************************************************************
// * VirtualAttrib::setFromData :
// *	This method will populate the VirtualAttrib object with the data contained in
// *	the cdevData object.
// *****************************************************************************
int VirtualAttrib::setFromData ( cdevData * data )
	{
	double val;
	int    result;
	if(data!=NULL)
		{
		result = CDEV_SUCCESS;
		data->get(UNITS_TAG, units, 255);
		if(data->get(CONTROLLOW_TAG, &val)==CDEV_SUCCESS)  setControlLow(val);
		if(data->get(CONTROLHIGH_TAG, &val)==CDEV_SUCCESS) setControlHigh(val);	
		if(data->get(ALARMLOW_TAG, &val)==CDEV_SUCCESS)    setAlarmLow(val);
		if(data->get(ALARMHIGH_TAG, &val)==CDEV_SUCCESS)   setAlarmHigh(val);	
		if(data->get(WARNINGLOW_TAG, &val)==CDEV_SUCCESS)  setWarningLow(val);
		if(data->get(WARNINGHIGH_TAG, &val)==CDEV_SUCCESS) setWarningHigh(val);	
		if(data->get(VALUE_TAG, &val)==CDEV_SUCCESS)
			{
			result=setValue(val);
			}
		}
	else result = CDEV_ERROR;
	checkAlarms();
	return result;
	}


// *****************************************************************************
// * VirtualAttrib::getToData :
// *	This method will populate the VirtualAttrib object with the data contained in
// *	the cdevData object.
// *****************************************************************************
void VirtualAttrib::getToData ( cdevData * data, cdevData * context )
	{
	if(data!=NULL)
		{
		data->remove();
		if(context!=NULL)
			{
			if(context->getType(VALUE_TAG)!=CDEV_INVALID)       data->insert(VALUE_TAG, getValue());
			if(context->getType(STATUS_TAG)!=CDEV_INVALID)      data->insert(STATUS_TAG, getStatus());
			if(context->getType(SEVERITY_TAG)!=CDEV_INVALID)    data->insert(SEVERITY_TAG, getSeverity());
			if(context->getType(UNITS_TAG)!=CDEV_INVALID)       data->insert(UNITS_TAG, getUnits());
			if(context->getType(CONTROLLOW_TAG)!=CDEV_INVALID)  data->insert(CONTROLLOW_TAG, getControlLow());
			if(context->getType(CONTROLHIGH_TAG)!=CDEV_INVALID) data->insert(CONTROLHIGH_TAG, getControlHigh());
			if(context->getType(ALARMLOW_TAG)!=CDEV_INVALID)    data->insert(ALARMLOW_TAG, getAlarmLow());
			if(context->getType(ALARMHIGH_TAG)!=CDEV_INVALID)   data->insert(ALARMHIGH_TAG, getAlarmHigh());
			if(context->getType(WARNINGLOW_TAG)!=CDEV_INVALID)  data->insert(WARNINGLOW_TAG, getWarningLow());
			if(context->getType(WARNINGHIGH_TAG)!=CDEV_INVALID) data->insert(WARNINGHIGH_TAG, getWarningHigh());
			}
		else
			{
			data->insert(VALUE_TAG, getValue());
			data->insert(STATUS_TAG, getStatus());
			data->insert(SEVERITY_TAG, getSeverity());
			}
		}
	}


// *****************************************************************************
// * VirtualAttrib::getAllToData :
// *	This method will populate the VirtualAttrib object with the data contained in
// *	the cdevData object.
// *****************************************************************************
void VirtualAttrib::getAllToData ( cdevData * data )
	{
	if(data!=NULL)
		{
		data->remove();
		data->insert(VALUE_TAG, getValue());
		data->insert(STATUS_TAG, getStatus());
		data->insert(SEVERITY_TAG, getSeverity());
		data->insert(UNITS_TAG, getUnits());
		data->insert(CONTROLLOW_TAG, getControlLow());
		data->insert(CONTROLHIGH_TAG, getControlHigh());
		data->insert(ALARMLOW_TAG, getAlarmLow());
		data->insert(ALARMHIGH_TAG, getAlarmHigh());
		data->insert(WARNINGLOW_TAG, getWarningLow());
		data->insert(WARNINGHIGH_TAG, getWarningHigh());
		}
	}

// *****************************************************************************
// * VirtualAttrib::setValue  :
// *	This method allows the caller to set the value of the Virtual Attrib.
// *	This call will fail if the specified value is outside of the legal
// *	range.
// *****************************************************************************
int VirtualAttrib::setValue ( double Value )
	{
	resultCode = CDEV_SUCCESS;

	if(controlHigh>controlLow && 
	  (Value<controlLow || Value>controlHigh)) 
	  	{
	  	resultCode = CDEV_OUTOFRANGE;
		}
	else if(value != Value)
		{
		value = Value;
		checkAlarms();
		if(monitors && monitors->isMonitored())
			{
			cdevData data;
			getAllToData(&data);
			monitors->fireMonitor(VALUE_TAG, &data);
			}
		}
	return resultCode;
	}

// *****************************************************************************
// * VirtualAttrib::setStatus  :
// *	This method allows the caller to set the status of the device.
// *****************************************************************************
int VirtualAttrib::setStatus ( char * Status )
	{
	if(strcmp(status, Status))
		{
		strncpy(status, Status, 255);
		status[254] = 0; 
		if(monitors && monitors->isMonitored())
			{
			cdevData data;
			getAllToData(&data);
			monitors->fireMonitor(STATUS_TAG, &data);
			}
		}
	return CDEV_SUCCESS;
	}
	
// *****************************************************************************
// * VirtualAttrib::setSeverity  :
// *	This method allows the caller to set the severity flag for the device.
// *****************************************************************************
int VirtualAttrib::setSeverity ( char * Severity )
	{
	if(strcmp(severity, Severity))
		{
		strncpy(severity, Severity, 255);
		severity[254] = 0; 
		if(monitors && monitors->isMonitored())
			{
			cdevData data;
			getAllToData(&data);
			monitors->fireMonitor(SEVERITY_TAG, &data);
			}
		}
	return CDEV_SUCCESS;	
	}
	
// *****************************************************************************
// * VirtualAttrib::setUnits  :
// *	This method allows the caller to set the units for the device.
// *****************************************************************************
int VirtualAttrib::setUnits ( char * Units )
	{
	if(strcmp(units, Units))
		{
		strncpy(units, Units, 255);
		units[254] = 0; 
		if(monitors && monitors->isMonitored())
			{
			cdevData data;
			getAllToData(&data);
			monitors->fireMonitor(UNITS_TAG, &data);
			}
		}
	return CDEV_SUCCESS;	
	}

// *****************************************************************************
// * VirtualAttrib::setAlarmHigh  :
// *	This method allows the caller to set the high alarm value of the device.
// *****************************************************************************
int VirtualAttrib::setAlarmHigh ( double AlarmHigh )
	{
	if(alarmHigh!=AlarmHigh)
		{
		alarmHigh = AlarmHigh;
		checkAlarms();
		if(monitors && monitors->isMonitored())
			{
			cdevData data;
			getAllToData(&data);
			monitors->fireMonitor(ALARMHIGH_TAG, &data);
			}
		}
	return CDEV_SUCCESS;
	}
	
// *****************************************************************************
// * VirtualAttrib::setAlarmLow  :
// *	This method allows the caller to set the low alarm value of the device.
// *****************************************************************************
int VirtualAttrib::setAlarmLow ( double AlarmLow )
	{
	if(alarmLow!=AlarmLow)
		{
		alarmLow = AlarmLow;
		checkAlarms();
		if(monitors && monitors->isMonitored())
			{
			cdevData data;
			getAllToData(&data);
			monitors->fireMonitor(ALARMLOW_TAG, &data);
			}
		}
	return CDEV_SUCCESS;
	}

// *****************************************************************************
// * VirtualAttrib::setWarningHigh  :
// *	This method allows the caller to set the high warning value of a device.
// *****************************************************************************
int VirtualAttrib::setWarningHigh ( double WarningHigh )
	{
	if(warningHigh!=WarningHigh)
		{
		warningHigh = WarningHigh;
		checkAlarms();
		if(monitors && monitors->isMonitored())
			{
			cdevData data;
			getAllToData(&data);
			monitors->fireMonitor(WARNINGHIGH_TAG, &data);
			}
		}
	return CDEV_SUCCESS;
	}

// *****************************************************************************
// * VirtualAttrib::setWarningLow   :
// *	This method allows the caller to set the low warning value of a device.
// *****************************************************************************
int VirtualAttrib::setWarningLow  ( double WarningLow )
	{
	if(warningLow != WarningLow)
		{
		warningLow = WarningLow;
		checkAlarms();
		if(monitors && monitors->isMonitored())
			{
			cdevData data;
			getAllToData(&data);
			monitors->fireMonitor(WARNINGLOW_TAG, &data);
			}
		}
	return CDEV_SUCCESS;
	}

// *****************************************************************************
// * VirtualAttrib::setControlHigh  :
// *	This method allows the caller to set the maximum value for a device.
// *****************************************************************************
int VirtualAttrib::setControlHigh ( double ControlHigh )
	{
	if(controlHigh != ControlHigh)
		{
		controlHigh = ControlHigh;
		checkAlarms();
		if(monitors && monitors->isMonitored())
			{
			cdevData data;
			getAllToData(&data);
			monitors->fireMonitor(CONTROLHIGH_TAG, &data);
			}
		}
	return CDEV_SUCCESS;
	}
	
// *****************************************************************************
// * VirtualAttrib::setControlLow   :
// *	This method allows the caller to set the minimum value of a device.
// *****************************************************************************
int VirtualAttrib::setControlLow  ( double ControlLow )
	{
	if(controlLow != ControlLow)
		{
		controlLow = ControlLow;
		checkAlarms();
		if(monitors && monitors->isMonitored())
			{
			cdevData data;
			getAllToData(&data);
			monitors->fireMonitor(CONTROLLOW_TAG, &data);
			}
		}
	return CDEV_SUCCESS;
	}

// *****************************************************************************
// * VirtualAttrib::checkAlarms  :
// *	This method allows the caller to read the value in comparison with all
// *	of its limits and set the status and severity tag appropriately.
// *****************************************************************************
void VirtualAttrib::checkAlarms ( void )
	{
	int done = 0;
	if(controlHigh>controlLow)
		{
		if(value<controlLow)
			{
			setStatus("OUT OF RANGE LOW");
			setSeverity("ERROR");
			done = 1;
			}
		else if (value>controlHigh)
			{
			setStatus("OUT OF RANGE HIGH");
			setSeverity("ERROR");
			done = 1;
			}
		}
	if(!done && alarmHigh>alarmLow)
		{
		if(value<alarmLow)
			{
			setStatus("ALARM LOW");
			setSeverity("ALARM");
			done = 1;
			}
		else if (value>alarmHigh)
			{
			setStatus("ALARM HIGH");
			setSeverity("ALARM");
			done = 1;
			}
		}
	if(!done && warningHigh>warningLow)
		{
		if(value<warningLow)
			{
			setStatus("WARNING LOW");
			setSeverity("WARNING");
			done = 1;
			}
		else if (value>warningHigh)
			{
			setStatus("WARNING HIGH");
			setSeverity("WARNING");
			done = 1;
			}
		}
	if(!done) 
		{
		setStatus("NORMAL");
		setSeverity("\0");
		}
	}
	
// *****************************************************************************
// * VirtualAttrib::insertMonitor :
// *	This message adds a monitor to the cdevMonitorTable for this device/
// *	attribute pair.  The message parameter becomes the property of the
// *	monitorTable and should not be accessed again by the caller.
// *****************************************************************************
void VirtualAttrib::insertMonitor ( cdevMonitorTable * table, cdevMessage * message )
	{
	if(table!=NULL && message!=NULL)
		{
		cdevData data;
		getAllToData(&data);
		table->insertMonitor(message, &data);	
		monitors = table->findMonitor(device, attrib);
		if(monitors && !monitors->isMonitored()) monitors = NULL;
		}
	else if(message!=NULL) delete message;
	}
	
// *****************************************************************************
// * VirtualAttrib::removeMonitor:
// *	This method uses the cancelTransIdx to locate and delete a monitor
// *	that was previously posted using that transaction index.
// *****************************************************************************
void VirtualAttrib::removeMonitor (cdevMonitorTable * table, cdevMessage * message )
	{
	if(table!=NULL && message!=NULL)
		{
		table->removeMonitor(message);
		if(monitors && !monitors->isMonitored()) monitors = NULL;
		}
	}
