/****************************************************************************/
/* Copyright (c) 2001 MBARI                                                 */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
/* Summary  :                                                               */
/* Filename : Ips4.cc                                                       */
/* Author   :                                                               */
/* Project  :                                                               */
/* Version  : 1.0                                                           */
/* Created  : 04/09/2001                                                    */
/* Modified :                                                               */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/****************************************************************************/
#include <unistd.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/uio.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 "Ips4.h"
#include "Syslog.h"

#define MaxRecordBytes 44
#define READ_TIMEOUT 3000
#define END_OF_RECORD "\xa"
#define AVERAGE_SOS  1450.0   // Average speed of sound used to calc range

Ips4::Ips4(SerialDevice *serialDevice, Boolean verbose)
     : SerialDeviceDriver("Ips4", serialDevice, MaxRecordBytes, END_OF_RECORD,
			  READ_TIMEOUT, 2)
{
  _verbose = verbose;
  _output = new Ips4Output(SharedData::ReadWrite);
  _iceDraft = new icedraft_t;
  _ipsRecord = (char*)malloc(41);
  strcpy(_ipsRecord, "");
  _log    = new Ips4Log(this, DataLog::BinaryFormat);
  _numberErrors = 0;
}


Ips4::~Ips4()
{
  stop();
  delete _output;
  delete _iceDraft;
  delete _log;
  free(_ipsRecord);
}


DeviceIF::Status Ips4::initialize()
{
  _numberErrors = 0;
  Syslog::write("Ips4:initializing Ips4...\n");

  char cmd[8];
  sprintf(cmd, "g\r");

  _device->flushReceiveBuf();

  // Tell unit to start processing. If accepted, it's good to go.
  //
  if (_device->write(cmd, strlen(cmd)) == strlen(cmd))
  {
    unsigned char record[MaxRecordBytes];
    int nbytes, attempts = 0;
    DeviceIF::Status stat;
    do
    {
      sleep(1);
      stat = readRecord((unsigned char*)record,
			MaxRecordBytes, END_OF_RECORD, 3000, &nbytes);
      if (stat == DeviceIF::Ok) break;
      attempts++;
      _device->write(cmd, strlen(cmd)) == strlen(cmd);
    } while (attempts < 4);

    free(record);
    if (stat == DeviceIF::Ok)
    {
      Syslog::write("Ips4:initialized\n");
      _output->data._deviceReady = True;
      writeOutput("initialize() OK");
      return DeviceIF::Ok;
    }
  }

  Syslog::write("Ips4:initialize failure\n");
  _output->data._deviceReady = False;
  writeOutput("initialize() Error");
  return DeviceIF::Error;
}
 
////////////////////////////////////////////////////////////////////////////////
//
// This routine overrides the readRecord declared in the base class, because 
// we don't get a terminating character.  We just look for 20 bytes each time.
//
////////////////////////////////////////////////////////////////////////////////
//
DeviceIF::Status Ips4::readRecord(unsigned char *record,
					   int maxRecordBytes,
					   const char *recordTerminator,
					   unsigned readTimeout,
					   int *nBytesRead)
{
  Boolean debug = _verbose;
  Boolean readError = False;

  char *eor = strdup(recordTerminator);

  if (_numberErrors > 7) // Make sure #errors > #attempts in initialize
  {
    Syslog::write("Ips4::readRecord(): Too many consecutive errors, reinit");
    initialize();
  }

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

  //  for (int ii = 0; ii < 43; ii++) printf("%x-", record[ii]);
  dprintf("Ips4::readRecord(): %d bytes => %s", *nBytesRead, record);

  if (!readError)
  {
    //
    // Compute the checksum for the record that we just read in:
    //
    unsigned char  byteVal, checksum = 0;
    short  advChecksum;

    for (short byte = 0; byte < 40; byte++)
    {
      sscanf( (char *)( record + byte ), "%c", &byteVal );
      checksum += byteVal;
    }
    //
    // Now, read in the checksum that the Ips computed, and compare with the
    // one that we computed here.
    //    
    sscanf( (char *) (record + 40), "%2x", 
	    &advChecksum);
    dprintf("Ips4::readRecord(): Computed checksum = %d, Ips checksum = %d",
	    checksum, advChecksum);

    if (checksum != advChecksum)
    {
      readError = True;
      Syslog::write("Ips4::readRecord() ensemble checksum incorrect");
      dprintf("Computed checksum = %d / Ips checksum      = %d\n",
	      checksum, advChecksum);
    }
  }

  if (readError)
  {
    _output->data._badComms = True;
    writeOutput("readRecord() Error");
    _numberErrors++;
    return DeviceIF::Error;
  }
  else
  {
    _numberErrors = 0;
    return DeviceIF::Ok;
  }

}

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

  int stat = decodeIPS(_iceDraft, (char*)record);
  if (stat < 0)
  {
    Syslog::write("Ips4::processRecord(): Bad status from decodeIPS: %d",
		  stat);
    return DeviceIF::Error;
  }

  // Use "standard" average SOS to caclulate range
  //
  _iceDraft->Range = (_iceDraft->TravelTime/2.) * AVERAGE_SOS;

  dprintf("Ips4::processRecord(): IPS data:");
  dprintf(" %02d/%02d/%02d %02d:%02d:%02d",
	  _iceDraft->Year, _iceDraft->Month, _iceDraft->Day,
	  _iceDraft->Hour, _iceDraft->Minute, _iceDraft->Second);
  dprintf(" Pressure = %f Temperature = %f",
	  (float)_iceDraft->Pressure, (float)_iceDraft->Temperature);
  dprintf(" X = %f Y = %f TT = %f Range = %f",
	  (float)_iceDraft->TiltX, (float)_iceDraft->TiltY,
	  (float)_iceDraft->TravelTime, (float)_iceDraft->Range);
  dprintf(" Amp = %u Persist = %f", 
	  _iceDraft->Amplitude, (float)_iceDraft->Persistence);
  dprintf(" Press Age = %02d Flags = %2d Status = %d",
	  _iceDraft->PressureAge, _iceDraft->Flag, _iceDraft->Status);

  _output->data._iceDraft = *_iceDraft;
  _output->data._badComms = False;    // If we got here, comms must be OK

  strncpy(_ipsRecord, (char*)record, (size_t)40);
  _ipsRecord[40] = '\0';

  writeOutput("readRecord()");

  // now write to log file
  _log->write();
  

  return DeviceIF::Ok;

}

////////////////////////////////////////////////////////////////////////////////
//
// This routine commands the Ips4 unit to stop processing. In this mode, the
// Ips4 will disable the sonar and suspend the real-time output in order to
// reduce power usage. However, pressure, temperature and tilt will continue
// to be sampled.
//
////////////////////////////////////////////////////////////////////////////////
//
DeviceIF::Status Ips4::stop()
{
  Syslog::write("Ips4:stopping Ips4...\n");

  char cmd[32];
  sprintf(cmd, "p\r");
  if (_device->write(cmd, strlen(cmd)) == strlen(cmd))
  {
    Syslog::write("Ips4:stopped\n");
    return DeviceIF::Ok;
  }
  else
  {
    Syslog::write("Ips4:stop failure\n");
    return DeviceIF::Error;
  }
}
 
////////////////////////////////////////////////////////////////////////////////
//
// This routine commands the Ips4 unit to start processing using the stored
// phase setup.
//
////////////////////////////////////////////////////////////////////////////////
//
DeviceIF::Status Ips4::start()
{
  Syslog::write("Ips4:starting Ips4...\n");

  char cmd[32];
  sprintf(cmd, "g\r");
  if (_device->write(cmd, strlen(cmd)) == strlen(cmd))
  {
    Syslog::write("Ips4:started\n");
    return DeviceIF::Ok;
  }
  else
  {
    Syslog::write("Ips4:start failure\n");
    return DeviceIF::Error;
  }
}

/////////////////////////////////////////////////////////////////////////
// calculate_IceDraft() - compute the icedraft using the following inputs
//
//	pAtm    - estimated value of the atmospheric pressure (in bars)
//    nPoints - number of elements in CTD array
//	cond    - pointer to conductivity array (C(S,T,0)/C(35,T,0))
//	temp    - pointer to temperature array (degress Celsius)
//	pres    - pointer to pressure array (absolute pressure in bars)
//
// output:
//      iceDraft- address of double to place calculated ice draft
//
// return:
//      non-zero Error code as defined in IceDraft.h
//      zero if completed normally
//
int Ips4::calculate_IceDraft(double pAtm, int nPoints,
			     double cond[/*nPoints*/], 
			     double temp[/*nPoints*/], 
			     double pres[/*nPoints*/],
			     double *iceDraft)
{
  double avgSOS, avgDens;
  int stat =0;

  // Compute the average speed of sound and density with the given inputs
  //
  stat = calcAvgProp(pAtm, nPoints, cond, temp, pres, &avgSOS, &avgDens);
  if (stat != 0)
    return stat;

  // Use the most current Ips4 data
  //
  icedraft_t ice = *_iceDraft;

  // Compute the ice draft and return
  //
  stat = IceDraft(&ice, pAtm, avgSOS, avgDens);
  if (stat != 0)
    return stat;

  *iceDraft = ice.Draft;
  return 0;
}

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