/****************************************************************************/
/* Copyright (c) 2001 MBARI                                                 */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
/* Summary  :                                                               */
/* Filename : Isus.cc                                                       */
/* Author   :                                                               */
/* Project  :                                                               */
/* Version  : 1.0                                                           */
/* Created  : 06/01/2001                                                    */
/* Modified :                                                               */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/****************************************************************************/
#include <unistd.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <i86.h>
#include <fcntl.h>
#include <errno.h>
#include <iostream.h>
#include <time.h>
#include <math.h>
#include <FloatAttribute.h>
#include <AttributeParser.h>
#include <StringAttribute.h>
#include <IntegerAttribute.h>
#include "Isus.h"
#include "Syslog.h"

#define MaxRecordBytes 100
#define READ_TIMEOUT 2000

Isus::Isus(SerialDevice *serialDevice, Boolean lampOn, Boolean verbose)
     : _verbose(verbose), _lampOn(lampOn),
       SerialDeviceDriver("Isus", serialDevice, MaxRecordBytes, "\n",
			  READ_TIMEOUT, 3)
{
  _output = new IsusOutput(SharedData::ReadWrite);
  _log    = new IsusLog(this, DataLog::BinaryFormat);
  _nitrate = 0.;
  _temp    = 0.;
  _quality = 0.;
}


Isus::~Isus()
{
  stop();
  delete _output;
  delete _log;
}


DeviceIF::Status Isus::initialize()
{
  Boolean debug = _verbose;

  _device->flushReceiveBuf();
  _device->clearPort();

  if (_lampOn)
    Syslog::write("Isus:initializing Isus with lamp on...");
  else
    Syslog::write("Isus:initializing Isus with lamp off...");

  char cmd[200];
  sprintf(cmd, "W\n");

  // Tell unit to wakeup. If accepted, it's good to go.
  //
  try {
  if (_device->write(cmd, strlen(cmd)) == strlen(cmd))
  {
    // Wait a short amount of time and then set the time of the
    // Isus unit to match the MVC time. Isus uses 1970 as the
    // beginning of time, as does qnx.
    //
    delay(1000);
    _device->readUntil(cmd, 200, "\n", 2000);
    dprintf("Isus:init():Wakeup - response:%s", cmd);

    sprintf(cmd, "W\n");
    if (_device->write(cmd, strlen(cmd)) == strlen(cmd))
    {
      _device->readUntil(cmd, 200, "\n", 2000);
      dprintf("Isus:init():Wakeup - response:%s", cmd);

      sleep(1);
      long t1970 = time((long*)0);
      if (setTime(t1970) == DeviceIF::Ok)
      {
	// Wait another small amount of time and start processing
	//
	_device->readUntil(cmd, 200, "\n", 2000);
	dprintf("Isus:init():Set time response:%s", cmd);

	Syslog::write("Isus:initialized");
	_output->data._deviceReady = True;
	writeOutput("initialize()");
	return start();
      }
      else
	Syslog::write("Isus:initialize(): time reset failure");
    }
  }
  }
  catch (Exception e)
  {
    Syslog::write("Isus::initialize() Exception: %s", e.msg);
  }

  stop();
  _output->data._deviceReady = False;
  writeOutput("initialize()");
  return DeviceIF::Error;
}
 
////////////////////////////////////////////////////////////////////////////////
//
//
////////////////////////////////////////////////////////////////////////////////
//
DeviceIF::Status Isus::readRecord(unsigned char *record,
					   int maxRecordBytes,
					   const char *recordTerminator,
					   unsigned readTimeout,
					   int *nBytesRead)
{
  Boolean debug = _verbose;
  Boolean readError = False;

  char *eor = strdup(recordTerminator);
  try 
  {
    //
    // Read data record from device
    //
    //    *nBytesRead = _device->readUntil((char *)record, maxRecordBytes,
    //			      eor, readTimeout);
    *nBytesRead = _device->readUntil((char *)record, 100, "\n", 2000);
  }
  catch (SerialDevice::TimedOut) {
    //    readError = True;
    Syslog::write("%s::readRecord() - Isus serial device timed out", name());
    Syslog::write("%s::readRecord() - record so far: %s", name(), record);
  }
  catch (SerialDevice::BufferFull) {
    //readError = True;
    _device->flushReceiveBuf();
    Syslog::write("%s::readRecord() - Isus serial device buffer full", name());
  }
  catch (Exception e) {
    readError = True;
    Syslog::write("%s::readRecord() - Isus driver caught exception:abort", name());
    throw;
  }

  delete eor;

#if 0
  //
  // Compute the checksum for the record that we just read in:
  //
  unsigned char  byteVal, checksum = 0;
  short  advChecksum;

  for (short byte = 0; byte < (*nBytesRead-3); byte++)
  {
    sscanf( (char *)( record + byte ), "%c", &byteVal );
    checksum += byteVal;
  }

  //
  // Now, read in the checksum that the Isus computed, and compare with the
  // one that we computed here.
  //    
  sscanf( (char *) (record + *nBytesRead - 3), "%2x", 
	  &advChecksum);
  dprintf("Isus::readRecord(): Computed checksum = %d, Isus checksum = %d",
	  checksum, advChecksum);

  if (checksum != advChecksum)
  {
    readError = True;
    Syslog::write("Isus WARNING : ensemble checksum incorrect\n"
		  "Computed checksum = %d\n"
		  "Isus checksum     = %d\n", checksum, advChecksum);
  }
#endif

  if (readError)
  {
    _output->data._badComms = True;
    writeOutput("readRecord()");
    return DeviceIF::Error;
  }
  else
  {
    dprintf("Isus::readRecord() good read of %d chars: %s\n", *nBytesRead, record);
    return DeviceIF::Ok;
  }

}

DeviceIF::Status Isus::processRecord(unsigned char *record, int nBytes)
{
  Boolean debug = _verbose;

  if (nBytes <= 0)
    return DeviceIF::Ok;

  _output->data._badComms = False;    // Assume comms will go OK


  // What kind of message is this?
  //
  char msgtype = record[0];
  char *nptr = NULL;
  switch(msgtype)
  {
  case 'A': // ACK  - acknowledging a command
    break;

  case 'N': // NACK - some kind of problem with the Isus
    Syslog::write("Isus: processRecord() - %s", record+5);
    _output->data._badComms = True;
    break;

  default:

    // Find the measurements in the record
    //
    // Skip the first part (date/time)
    //
    char *rec = strdup((char *)record);

    nptr = strtok(rec, ",");
    if (nptr == NULL)
    {
      Syslog::write("Isus: processRecord() - bad date/time: %s", record);
      break;
    }

    // Read temperature
    //
    nptr = strtok(NULL, ",");
    if (nptr == NULL)
    {
      Syslog::write("Isus: processRecord() - bad temperature data: %s", record);
      break;
    }

    int nitems;
    double temp;
    nitems = sscanf(nptr, "%lf", &temp);
    if (nitems == 1)
    {
      dprintf("Isus:processRecord(): Temp value=%lf", temp);
      _temp = temp;
    }

    // Read nitrate
    //
    nptr = strtok(NULL, ",");
    if (nptr == NULL)
    {
      Syslog::write("Isus: processRecord() - bad nitrate data: %s", record);
      break;
    }

    double nitrate;
    nitems = sscanf(nptr, "%lf", &nitrate);
    if (nitems == 1)
    {
      dprintf("Isus:processRecord(): Nitrate value=%lf", nitrate);
      _nitrate = nitrate;
    }

    // Read quality
    //
    nptr = strtok(NULL, ",");
    if (nptr == NULL)
    {
      Syslog::write("Isus: processRecord() - bad quality data: %s", record);
      break;
    }

    double quality;
    nitems = sscanf(nptr, "%lf", &quality);
    if (nitems == 1)
    {
      dprintf("Isus:processRecord(): Quality value=%lf", quality);
      _quality = quality;
    }

    free (rec);
    break;

  }

  try
  {
    writeOutput("processRecord()");
  }
  catch (SharedData::AccessError error)
  {
    fprintf(stderr, "Isus::processRecord(): SharedData::write() error");
    exit(1);
  }

  _log->write();

  return DeviceIF::Ok;

}

////////////////////////////////////////////////////////////////////////////////
//
//
DeviceIF::Status Isus::stop()
{
  Syslog::write("Isus:stopping Isus...");

  _device->flushReceiveBuf();
  
  char cmd[100];
  sprintf(cmd, "S\n");
  if (_device->write(cmd, strlen(cmd)) == strlen(cmd))
  {
    delay(200);
    _device->flushReceiveBuf();
    _device->readUntil(cmd, 100, "\n", 2000);
    Syslog::write("Isus:stopped - response %s", cmd);
    return DeviceIF::Ok;
  }
  else
  {
    Syslog::write("Isus:stop failure");
    return DeviceIF::Error;
  }
}
 
///////////////////////////////////////////////////////////////////////////////
//
// This routine commands the Isus unit to start processing using the stored
// phase setup.
//
///////////////////////////////////////////////////////////////////////////////
//
DeviceIF::Status Isus::start()
{
  Syslog::write("Isus:starting Isus...");

  char cmd[100];

  if (_lampOn)
    sprintf(cmd, "D\n");
  else
    sprintf(cmd, "R\n");

  if (_device->write(cmd, strlen(cmd)) == strlen(cmd))
  {
    _device->readUntil(cmd, 100, "\n", 2000);
    Syslog::write("Isus:started");
    return DeviceIF::Ok;
  }
  else
  {
    Syslog::write("Isus:start failure");
    return DeviceIF::Error;
  }
}

void Isus::writeOutput(char *msg)
{
  try
  {
    _output->write();
  }
  catch (SharedData::AccessError error)
  {
    fprintf(stderr, "Isus::%s: SharedData::write() error", msg);
    exit(1);
  }
}

////////////////////////////////////////////////////////////////////////////////
//
// This routine commands the Isus unit to reset it's internal clock to a given
// time. The input is the number of senconds since 1970/01/01 00:00:00.
//
////////////////////////////////////////////////////////////////////////////////
//
DeviceIF::Status Isus::setTime(unsigned long t1970)
{
  Boolean debug = _verbose;

  // Send a "T" command and then wait for the device to
  // ready itself for the arguments.
  //
  char cmd[100];
  sprintf(cmd, "T");

  if (_device->write(cmd, strlen(cmd)) == strlen(cmd))
  {
    sleep(1);

    // Now send the command arguments one character at a time
    // with a small delay in between each.
    //
    sprintf(cmd, ",%ld\n", t1970);
    int i, cmdlen = strlen(cmd);
    for (i = 0; i < cmdlen; i++)
    {
      dprintf("%c", cmd[i]);
      if (_device->write(cmd+i, 1) != 1)
      {
	// Something bad happened, abort
	//
	break;
      }
      delay (100);
    }

    // Did we complete? Great. Return an Ok.
    //
    if (i == cmdlen)
    {
      Syslog::write("Isus:time set with val %ld -> %s", 
		    (long)t1970, asctime(localtime((long*)&t1970)));
      return DeviceIF::Ok;
    }
  }

  // Something failed along the way.
  //
  Syslog::write("Isus:time reset failure (%s)", cmd);
  return DeviceIF::Error;

}
