#include "MetraByte.h"
#define BAUD 9600
#define PARITY "NONE"
#define STOP_BITS 1
#define DATA_BITS 8
#define READ_TIMEOUT 750

#define GetVoltageCmd "$4RD\xd\xa"
#define GetCurrentCmd "$2RD\xd\xa"
// To calculate current we must multiply the voltage read by the MB by 2.24 
// (note discrepancy between metrabyte documentation and actual resistor values)

#define CURRENTFACTOR 2.24 

#define METRABYTE_PERIOD 1000

MetraByte::MetraByte(SerialDevice *device)
  : PeriodicTask("MetraByte")
{
  _device = device;
  if (init() == -1) {
    Syslog::write("MetraByte: init failed!\n");
    throw Exception("MetraByte -- init failed!\n");
  }
  addPeriodicCallback(METRABYTE_PERIOD, (CallbackMethod)MetraByte::sample);
  _log = new MetraByteLog(this, DataLog::BinaryFormat);

  _output->data.current = _output->data.voltage = 0.;
}


MetraByte::~MetraByte()
{
  delete _output;
}


DeviceIF::Status MetraByte::init()
{
  char buf[30];
  
  _output = new MetraByteOutput();

  _output->data.current = 0.;
  _output->data.voltage = 0.;
  _output->data.sampleTime.tv_sec = 0;
  _output->data.sampleTime.tv_nsec = 0;

  try {
    _output->write();
  }

  catch (SharedData::AccessError error) {
    fprintf(stderr, "%s",error.msg);
    return DeviceIF::Error;
  }
  
  _device->commsDebugMode(SerialDevice::DebugOff);
  _device->printTermios();

  
  // initialize serial port
  _device->setLineFormat(BAUD, DATA_BITS, STOP_BITS, PARITY);
  
  // clear serial port
  
  _device->clearPort();
  _device->flushReceiveBuf();
  
  return DeviceIF::Ok;
} 


void MetraByte::sample(void)
{
  getData(MetraByte::CurrentData);
  getData(MetraByte::VoltageData);
  
  // See note above
  //sigh
  _output->data.current = (_output->data.current * CURRENTFACTOR);
  clock_gettime(CLOCK_REALTIME, &_output->data.sampleTime);
  _output->write();
  _log->write();
}



int MetraByte::getData(MetraByte::DataType dataType)
{
  char cmd[16];
  float *dataPtr;

  switch (dataType) {
  case CurrentData:
    strcpy(cmd, GetCurrentCmd);
    dataPtr = &_output->data.current;
    break;

  case VoltageData:
    strcpy(cmd, GetVoltageCmd);
    dataPtr = &_output->data.voltage;
    break;

  default:
    Syslog::write("MetraByte::getData() - unknown data type requested");
    return -1;
  }

  // Request the data
  _device->write(cmd, strlen(cmd));

  // Read and parse the reply
  char reply[16];
  try {
    _device->readUntil("*", READ_TIMEOUT);
    _device->readUntil(reply, sizeof(reply), "\xa", READ_TIMEOUT);

    if (sscanf(reply, "%f", dataPtr) == 1) {
      return 0;
    }
    else {
      Syslog::write("MetraByte::getData() - sscanf() of \"%s\" failed\n", 
		    reply);
      return -1;
    }
  }
  catch (Exception e) {
    Syslog::write("MetraByte::getVoltage() - caught exception: %s\n", e.msg);
    return -1;
  }

  return 0;
}

