// sensor.cpp: implementation of the sensor related classes.
//
//////////////////////////////////////////////////////////////////////

#include "sensor.h"
#include "debug.h"
#include <string.h>
#include "debug.h"
#include "osutils.h"
#include <malloc.h>
#include "errorlog.h"
#include "dlportio.h"


/////////////////////////////////////////////////////////////////////////////
// Veriteq local definitions.
/////////////////////////////////////////////////////////////////////////////

// Define the API version
// These are normally read from the lrs.cfg file
// These are defaults used in the absence of lrs.cfg
#define API_VERSION_DEFAULT "V3.5-Beta3"

// Define a macro for checking the status code returned from the 
// Spectrum API routines.
#define CHECK_STATUS_CODE(StatusCode)                     \
{                                                         \
   if ((StatusCode) != SPC_SC_Success)                    \
   {                                                      \
      char szStatusText[512];                             \
      SPUTL_FormatStatusCode((StatusCode), szStatusText); \
	  ErrorLog::logError(szStatusText);                   \
      return(StatusCode);                                 \
   }                                                      \
}


//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

VeriteqSensor::VeriteqSensor()
:_connected(FALSE)
{
	

}

VeriteqSensor::~VeriteqSensor()
{


}

int VeriteqSensor::init(const char *name, LrsCfg *config)
{ 	
   char szBuffer[256]; 	
   char apiver[256];
	
	// Init defaults
	_lComPortNumber = 0; //default to com0 port
	strcpy(apiver, API_VERSION_DEFAULT);

   // Get the COM port number, API version, and date
   if(config != NULL) {
	      ConfigItemWrapper cfgitem;
		  
		  if(config->searchForConfigItem(name, cfgitem) && cfgitem()->nitems() >= 2) {			  
			  memset(apiver, NULL, sizeof(apiver)); 
			  strcpy(apiver,cfgitem()->parm(API_VER));
			  _lComPortNumber = (SPT_lComPortNumber) atoi(cfgitem()->parm(COM_PORT));
		  }
   }   

   // Check the version
   _lStatusCode = SPAPI_GetApiVersion(szBuffer);
   CHECK_STATUS_CODE(_lStatusCode);
   if (strcmp(szBuffer, apiver) != 0)
   {
	  ErrorLog::logError("ERROR: Invalid API version detected");
      return(-1);
   }

   // Set the logger address.
   _ulLoggerAddress = 0;

   // Enable Phase 1 communication.
   _lStatusCode = SPAPI_SetProtocol(FALSE);
   CHECK_STATUS_CODE(_lStatusCode);

   // Create a logger object.
   _lStatusCode = SPLGR_CreateLoggerObject(&_ulLoggerHandle);
   CHECK_STATUS_CODE(_lStatusCode);

    // If can read channel values
   _lStatusCode = SPLGR_ReadChannelValues(_ulLoggerHandle, 
                                            _lComPortNumber,
                                            _ulLoggerAddress);
   CHECK_STATUS_CODE(_lStatusCode);

    // Check the description.
   _lStatusCode = SPLGR_GetDescription(_ulLoggerHandle, szBuffer);
   CHECK_STATUS_CODE(_lStatusCode);
   if (strcmp(szBuffer, name) != 0)
   {
	  ErrorLog::logError("Invalid description detected");
      return(-1);
   }

   //are connected correctly
   _connected =  TRUE;

   return 0;
}

//Sample temp and return temp data in degrees Celcius
//TODO: Throws exception VeriteqSensor::badTemp if any errors reading VeriteqSensor
//otherwise returns good data
int VeriteqSensor::sampleTemp(TempType *temp)
{	
	//  Temp is on channel 0
	return this->readChannel(0, (TempType *) temp);
}
//Sample temp and return temp data in degrees Celcius
//TODO: Throws exception VeriteqSensor::badTemp if any errors reading VeriteqSensor
//otherwise returns good data
int VeriteqSensor::sampleRelativeHumidity(RhType *rh)
{	
	//  Relative humidity is on channel 1
	return this->readChannel(1, (RhType *) rh);
}

HousekeepingSensors::HousekeepingSensors(const char *name)
{
	_name = strdup(name);	
	_baseaddress = 0x378; //port 0
	_bitmask = 0x40; //status bit 7/bit 10
}

HousekeepingSensors::~HousekeepingSensors()
{
	free((void*) _name);
}
HousekeepingSensors::BadTempReading::BadTempReading(const char *descr)
	:Exception(BADTEMPREADING_EXCEPTION, descr)
{
}
void HousekeepingSensors::sample(HousekeepingSensors::Data *data)
{
	data->temp = (TempType) OUTOFRANGE;
	data->rh = (RhType) OUTOFRANGE;	

	DPRINTF("Sampling temp for %s\n", _name);

	if(_vsensor.sampleTemp(&data->temp) == -1) {
		data->temp = OUTOFRANGE;
		//_THROW_(BadTempReading("Bad temp reading\n"));
	}
	
	DPRINTF("Sampling relative humidity for %s\n", _name);

	if(_vsensor.sampleRelativeHumidity(&data->rh) == -1) {
		data->rh = OUTOFRANGE;
		//_THROW_(BadRhReading("Bad rh reading\n"));
	}

	data->wateralarm = this->sampleWaterSensor();
}

BOOL HousekeepingSensors::sampleWaterSensor()
{
	// Using Parallel port status word which is base address + 1
	// Water alarm is active low, so invert
	if(_baseaddress >= 0x278) //must be above priveleged address range 
	return (!(_bitmask & (char) DlPortReadPortUchar(_baseaddress + 1)) ? TRUE:FALSE);
	return 0;
}
// Read Veriteq sensor
int VeriteqSensor::readChannel(int chan, double *data)
{
   char     szBuffer[512];
   double	dChannelValue;

   if(_connected)
	// Read the channel values from the logger.
	_lStatusCode = SPLGR_ReadChannelValues(_ulLoggerHandle, 
                                            _lComPortNumber,
                                            _ulLoggerAddress);
   else
	   return -1;

	if (_lStatusCode != SPC_SC_Success)      {
		SPUTL_FormatStatusCode(_lStatusCode, szBuffer);
		CHECK_STATUS_CODE(_lStatusCode);
		return -1;
	}
	else
	{
		  // Get the channel enabled flag.
		BOOL bChannelEnabled;

		// Clear the print buffer.
		szBuffer[0] = 0;	

		_lStatusCode = SPLGR_GetChannelEnabled(_ulLoggerHandle, 
											(SPT_lChannelIndex) chan, 
											&bChannelEnabled);
		
		// See if the channel is enabled.
		if (bChannelEnabled)   {
            
			// Get the channel value.
			_lStatusCode = SPLGR_GetChannelValue(_ulLoggerHandle,
                                                  (SPT_lChannelIndex) chan,
                                                   &dChannelValue);
			
			CHECK_STATUS_CODE(_lStatusCode);
			
			// Get the channel units.
			char szUnits[512];
			_lStatusCode = SPLGR_GetChannelUnits(_ulLoggerHandle, 
                                                   (SPT_lChannelIndex) chan, 
                                                   szUnits);
			
			CHECK_STATUS_CODE(_lStatusCode);
			
			// Display the channel value and the channel units.
			sprintf(&szBuffer[strlen(szBuffer)],
                       "%12.2lf %-4.4s", 
                       dChannelValue, 
                       szUnits);

			
			// Output the print buffer.
			DPRINTF("%s\n", szBuffer);
		}
	}

	*data = dChannelValue;
	return 0;
}

int HousekeepingSensors::init(const char *name, LrsCfg *cfg)
{
	// Store return code from veriteq sensors init
	int rc = this->_vsensor.init(name, cfg);
	
	// Default to parallel port 0 and status bit 7/ pin 10/ Parallel ack line 
	_baseaddress = 0x378; //port 0
	_bitmask = 0x40; //status bit 7/bit 10

   // Get base address and bit mask from lrs.cfg file
   if(cfg != NULL) {
	      ConfigItemWrapper cfgitem;
		  
		  if(cfg->searchForConfigItem(name, cfgitem) && cfgitem()->nitems() 
			  == NUMHOUSEKEEPING_CFGITEMS) {			  
			  // If error finding base address TODO: log error message here
			  if(sscanf(cfgitem()->parm(BASE_ADDR),"0x%x",&_baseaddress) == -1) 
				  if(sscanf(cfgitem()->parm(BASE_ADDR),"0X%x",&_baseaddress) == -1) 
					  _baseaddress = 0x378; 

			  // If error finding bit mask TODO: log error message here
			  if(sscanf(cfgitem()->parm(BIT_MASK),"0x%x",&_bitmask) == -1)
				  if(sscanf(cfgitem()->parm(BIT_MASK),"0X%x",&_bitmask) == -1)
					  _bitmask = 0x40; 
		  }
   }   

   return rc;

}
