/****************************************************************************/
/* Copyright (c) 2002 MBARI                                                 */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
/* Summary  :                                                               */
/* Filename : Lisst.cc                                                      */
/* Author   :                                                               */
/* Project  :                                                               */
/* Version  : 1.0                                                           */
/* Created  : 07/09/2002                                                    */
/* Modified :                                                               */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/****************************************************************************/
#include <unistd.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <i86.h>
#include <fcntl.h>
#include <errno.h>
#include <iostream.h>
#include <time.h>
#include "TimeP.h"

#include <math.h>
#include <FloatAttribute.h>
#include <AttributeParser.h>
#include <StringAttribute.h>
#include <IntegerAttribute.h>
#include "Lisst.h"
#include "Syslog.h"

#define READ_TIMEOUT 1000   // milliseconds
#define MAX_RECORD_BYTES 500
#define EXPECTED_RECORD_LENGTH 243  // 40 values @ 6 chars each, plus CRLF, plus '*'
#define EXPECTED_NVALS  40  // expected number of items in a data record
#define RECORD_TERMINATOR "\xa"

////////////////////////////////////////////////////////////////
// Lisst ctor
////////////////////////////////////////////////////////////////
Lisst::Lisst(SerialDevice *serialDevice, Boolean verbose)
     : _verbose(verbose),
       SerialDeviceDriver("Lisst", serialDevice, MAX_RECORD_BYTES, RECORD_TERMINATOR,
			  READ_TIMEOUT, 3)
{
  _log    = new LisstLog(this, DataLog::BinaryFormat);
}


////////////////////////////////////////////////////////////////
// Lisst dtor
////////////////////////////////////////////////////////////////
Lisst::~Lisst()
{
  stop();
  delete _log;
}


////////////////////////////////////////////////////////////////
// Lisst initialization function - prepare device for data
// collection
////////////////////////////////////////////////////////////////
DeviceIF::Status Lisst::initialize()
{
  Boolean debug = _verbose;

  char cmd[200];
  char response[300];
  int  nbytes = 0;

  // Send a CRLF to acquire the prompt
  //
  sprintf(cmd, "\xd\xa");
  if (_device->write(cmd, strlen(cmd)) != strlen(cmd))
  {
    return DeviceIF::Error;
  }

  // Flush the echo and prompt
  //
  delay(1000);
  flushReadBuffer((unsigned char*)response, 299, &nbytes); response[nbytes]='\0';
  dprintf("%s read after \\n", response);

  // RUN the program currently loaded in the LISST memory
  // IMPORTANT! The program must be BIOLUME.TTB, or compatible
  //
  sprintf(cmd, "RUN\xd\xa");
  if (_device->write(cmd, strlen(cmd)) != strlen(cmd))
  {
    return DeviceIF::Error;
  }

  // Flush the echo and prompt
  //
  delay(1000);
  flushReadBuffer((unsigned char*)response, 299, &nbytes); response[nbytes]='\0';
  dprintf("%s read after RUN", response);

  // Choose option 2 to set the date/time of the instrument
  //
  sprintf(cmd, "2\xd\xa");
  if (_device->write(cmd, strlen(cmd)) != strlen(cmd))
  {
    return DeviceIF::Error;
  }

  // Flush the echo and prompt
  //
  delay(1000);
  flushReadBuffer((unsigned char*)response, 299, &nbytes); response[nbytes]='\0';
  dprintf("%s read after 2", response);

  // Reset the unit's date and time
  //
  if (setTime(time((long*)0)) != DeviceIF::Ok)
  {
    return DeviceIF::Error;
  }

  // Start the flow of data
  //
  return start();

}

///////////////////////////////////////////////////////////////
// flushReadBuffer - reads whatever is in the read buffer and
// returns it in record buffer
///////////////////////////////////////////////////////////////
DeviceIF::Status Lisst::flushReadBuffer(unsigned char *record,
					int maxRecordBytes,
					int *nBytesRead)
{
  Boolean debug = _verbose;
  *nBytesRead = 0;

  try 
  {
    //
    // Read whatever data is in the read buffer
    //
    *nBytesRead = _device->readNChars((char *)record,
				      min(maxRecordBytes,_device->nRecvdBytes()), 1000);
  }
  catch (SerialDevice::TimedOut) {
    *nBytesRead = 0;
    dprintf("%s::readRecord() - serial device timed out", name());
    Syslog::write("%s::readRecord() - record so far: %s", name(), record);
  }
  catch (SerialDevice::BufferFull) {
    *nBytesRead = maxRecordBytes;
    Syslog::write("%s::readRecord() - serial device buffer full", name());
  }
  catch (Exception e) {
    *nBytesRead = 0;
    Syslog::write("%s::readRecord() - driver caught exception:%s",
		  name(), e.msg);
    throw;
  }

  return DeviceIF::Ok;
}

////////////////////////////////////////////////////////////////////////////////
// readRecord() - read data from the device. Return Ok if a valid record was
//                acquired, otherwise Error.
////////////////////////////////////////////////////////////////////////////////
//
DeviceIF::Status Lisst::readRecord(unsigned char *record,
					   int maxRecordBytes,
					   const char *recordTerminator,
					   unsigned readTimeout,
					   int *nBytesRead)
{
  Boolean debug = _verbose;
  Boolean readError = False;

  try 
  {
    //
    // Read data record from device
    //
    *nBytesRead = _device->readNChars((char *)record, EXPECTED_RECORD_LENGTH, readTimeout);

  }
  catch (SerialDevice::TimedOut) {
    readError = True;
    dprintf("%s::readRecord() - serial device timed out", name());
    Syslog::write("%s::readRecord() - record so far: %s", name(), record);
  }
  catch (SerialDevice::BufferFull) {
    readError = True;
    Syslog::write("%s::readRecord() - serial device buffer full", name());
  }
  catch (Exception e) {
    readError = True;
    Syslog::write("%s::readRecord() - driver caught exception:%s",
		  name(), e.msg);
    throw;
  }

  dprintf("%s::readRecord(): %d bytes => %s", name(), *nBytesRead, record);

  if (readError)
  {
    return DeviceIF::Error;
  }
  else
  {
    return DeviceIF::Ok;
  }

}

////////////////////////////////////////////////////////////////////////////////
// processRecord() - parse the data record into member variables
//
////////////////////////////////////////////////////////////////////////////////
//
DeviceIF::Status Lisst::processRecord(unsigned char *record, int nBytes)
{
  Boolean debug = _verbose;

  // Format string for scanf - a bunch of integers
  //
  char *recFormat =
    "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d";

  // Make sure the record is the correct length
  //
  if (nBytes < EXPECTED_RECORD_LENGTH)
  {
    Syslog::write("Lisst processRecord(): record too short(%d)", nBytes);
    return DeviceIF::Error;
  }

  // Read the values from the string and log them
  //
  int nvals = sscanf((const char*)record+1, recFormat, // record+1 because '*' not always present
		     &_det1,  &_det2,  &_det3,  &_det4,  &_det5,  &_det6,  &_det7,  &_det8,
		     &_det9,  &_det10, &_det11, &_det12, &_det13, &_det14, &_det15, &_det16,
		     &_det17, &_det18, &_det19, &_det20, &_det21, &_det22, &_det23, &_det24,
		     &_det25, &_det26, &_det27, &_det28, &_det29, &_det30, &_det31, &_det32,
		     &_laserTrans, &_volt, &_aux1, &_laserRef, &_pressure, &_temp,
		     &_time1, &_time2);

  if (nvals < EXPECTED_NVALS)
  {
    Syslog::write("Lisst processRecord(): parsing error - only read %d values", nvals);
    return DeviceIF::Error;
  }
  else
  {
    _log->write();
    return DeviceIF::Ok;
  }

}

////////////////////////////////////////////////////////////////////////////////
// Tell the LISST to stop processing
//
DeviceIF::Status Lisst::stop()
{
  DeviceIF::Status returnStat = DeviceIF::Ok;

  Syslog::write("Lisst:stopping Lisst...");

  // Send a ctrl-c, then RUN to reset  
  char cmd[100];
  cmd[0] = 3;
  cmd[1] = '\0';

  if (_device->write(cmd, strlen(cmd)) == strlen(cmd))
  {
    delay(500);
    sprintf(cmd, "RUN\xd\xa");
    
    if (_device->write(cmd, strlen(cmd)) == strlen(cmd))
    {
      returnStat = DeviceIF::Ok;
    }
    else
    {
      returnStat = DeviceIF::Error;
    }
  }
  else
  {
    Syslog::write("Lisst:stop failure");
    returnStat = DeviceIF::Error;
  }

  delay(500);

  return returnStat;
}
 
///////////////////////////////////////////////////////////////////////////////
//
// This routine commands the unit to start processing
//
///////////////////////////////////////////////////////////////////////////////
//
DeviceIF::Status Lisst::start()
{
  Boolean debug = _verbose;

  Syslog::write("Lisst:starting Lisst...");

  char cmd[100];
  char response[300];
  int nbytes = 0;

  // Select option "1" - start collecting data
  //
  delay(500);

  flushReadBuffer((unsigned char*)response, 299, &nbytes); response[nbytes]='\0';
  dprintf("%s read before 1", response);

  sprintf(cmd, "1\xd\xa");
  if (_device->write(cmd, strlen(cmd)) == strlen(cmd))
  {
    Syslog::write("Lisst:started");

    delay(500);
    flushReadBuffer((unsigned char*)response, 299, &nbytes); response[nbytes]='\0';
    dprintf("%s read after 1", response);

    return DeviceIF::Ok;
  }
  else
  {
    Syslog::write("Lisst:start failure");
    return DeviceIF::Error;
  }
}

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

  // Use the DATE command to reset the unit's date/time to match the MVC's
  //
  char cmd[100];
  char response[300];
  int nbytes = 0;

  // Delays are used to sync with device
  //
  delay(500);
  flushReadBuffer((unsigned char*)response, 299, &nbytes); response[nbytes]='\0';
  dprintf("%s read after \\n", response);

  // Get the localtime
  //
  struct tm *hytime = localtime((time_t*)&t1970);
  sprintf(cmd, "%02d/%02d/%4d %02d:%02d:%02d\xd\xa",
	  hytime->tm_mon+1, hytime->tm_mday, hytime->tm_year+1900,
	  hytime->tm_hour,  hytime->tm_min,  hytime->tm_sec);
  dprintf("Setting LISST time to %s", cmd);

  // Use the local time values to answer the awaiting prompts from the device
  //
  sprintf(cmd, "%4d\xd\xa", hytime->tm_year+1900);
  if (_device->write(cmd, strlen(cmd)) != strlen(cmd))
  {
    return DeviceIF::Error;
  }

  delay(50);
  flushReadBuffer((unsigned char*)response, 299, &nbytes); response[nbytes]='\0';
  dprintf("%s read after YEAR", response);

  sprintf(cmd, "%2d\xd\xa", hytime->tm_mon+1);
  if (_device->write(cmd, strlen(cmd)) != strlen(cmd))
  {
    return DeviceIF::Error;
  }

  delay(50);
  flushReadBuffer((unsigned char*)response, 299, &nbytes); response[nbytes]='\0';
  dprintf("%s read after MONTH", response);

  sprintf(cmd, "%2d\xd\xa", hytime->tm_mday);
  if (_device->write(cmd, strlen(cmd)) != strlen(cmd))
  {
    return DeviceIF::Error;
  }

  delay(50);
  flushReadBuffer((unsigned char*)response, 299, &nbytes); response[nbytes]='\0';
  dprintf("%s read after DAY", response);

  sprintf(cmd, "%2d\xd\xa", hytime->tm_hour);
  if (_device->write(cmd, strlen(cmd)) != strlen(cmd))
  {
    return DeviceIF::Error;
  }

  delay(50);
  flushReadBuffer((unsigned char*)response, 299, &nbytes); response[nbytes]='\0';
  dprintf("%s read after HOUR", response);

  sprintf(cmd, "%2d\xd\xa", hytime->tm_min);
  if (_device->write(cmd, strlen(cmd)) != strlen(cmd))
  {
    return DeviceIF::Error;
  }

  delay(50);
  flushReadBuffer((unsigned char*)response, 299, &nbytes); response[nbytes]='\0';
  dprintf("%s read after MINIUTES", response);

  sprintf(cmd, "%2d\xd\xa", hytime->tm_sec);
  if (_device->write(cmd, strlen(cmd)) != strlen(cmd))
  {
    return DeviceIF::Error;
  }

  delay(500);
  flushReadBuffer((unsigned char*)response, 299, &nbytes); response[nbytes]='\0';
  dprintf("%s read after SECONDS", response);

  return DeviceIF::Ok;

}

