/****************************************************************************/
/* Copyright (c) 2000 MBARI                                                 */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
/* Summary  :                                                               */
/* Filename : SerialDeviceDriver.cc                                         */
/* Author   :                                                               */
/* Project  :                                                               */
/* Version  : 1.0                                                           */
/* Created  : 02/07/2000                                                    */
/* Modified :                                                               */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/****************************************************************************/
#include <string.h>
#include <signal.h>
#include <malloc.h>
#include "SerialDeviceDriver.h"
#include "Exception.h"
#include "Syslog.h"

SerialDeviceDriver::SerialDeviceDriver(const char *name,
				       SerialDevice *serialDevice,
				       unsigned maxRecordBytes,
				       const char *recordTerminator,
				       unsigned readTimeout,
				       unsigned maxInitTries)
{
  char errorBuf[512];

  _name = strdup(name);

  _device = serialDevice;

  // Container task not yet assigned
  _containerTask = 0;

  _recordTerminator = strdup(recordTerminator);
  _maxRecordBytes = maxRecordBytes;

  _readTimeout = readTimeout;

  _state = DeviceIF::Initializing;

  _maxInitTries = maxInitTries;

  // Allocate record buffer
  if ((_record = (unsigned char *)malloc(_maxRecordBytes)) == 0) {
    sprintf(errorBuf, 
	    "SerialDeviceDriver::SerialDeviceDriver() for %s - "
	    "malloc() of %d bytes failed", name, _maxRecordBytes);

    throw Exception(errorBuf);
  }

  signal(SIGHUP, SIG_IGN);
}


SerialDeviceDriver::~SerialDeviceDriver()
{
  free((void *)_recordTerminator);
  free((void *)_record);
  free((void *)_name);
}


unsigned SerialDeviceDriver::readTimeout()
{
  return _readTimeout;
}


void SerialDeviceDriver::setReadTimeout(unsigned timeout)
{
  _readTimeout = timeout;
}


DeviceIF::Status SerialDeviceDriver::startup()
{
  char errorBuf[512];
  Boolean debug = True;
  dprintf("SerialDeviceDriver::initialize()");

  DeviceIF::Status newState;

  // Try to initialize device
  for (int i = 0; i < _maxInitTries; i++) {
    // Initialize device; pure virtual method
    if ((newState = initialize()) == DeviceIF::Ok)
      break;
  }

  setState(newState);

  if (state() != DeviceIF::Ok) {
    // initialize() failed
    sprintf(errorBuf, "%s::initialize() failed after %d tries", 
	    name(), _maxInitTries);

    throw Exception(errorBuf);
  }

  try {
    _device->flushReceiveBuf();
  }
  catch (...) {
    setState(DeviceIF::Error);
  }

  return state();
}


DeviceIF::Status SerialDeviceDriver::acquire()
{
  Boolean debug = False;
  char errorBuf[512];
  DeviceIF::Status newState;

  int nBytesRead = 0;

  // Prepare for data acquisition, etc
  newState = beginCycle();
  setState(newState);

  dprintf("SerialDeviceDriver::acquire() - call readRecord()");

  // Read data record from device
  newState = readRecord(_record, _maxRecordBytes, _recordTerminator,
			_readTimeout, &nBytesRead);

  setState(newState);

  if (newState == DeviceIF::Ok) {
    // Process device data record; pure virtual method
    newState = processRecord(_record, nBytesRead);
    setState(newState);
  }

  // Check for command, then process it
  newState = processCommand();
  setState(newState);
  return state();
}



Boolean SerialDeviceDriver::setState(DeviceIF::Status newState)
{
  if (newState == _state) {
    // No change in state
    return False;
  }

  _state = newState;

  // Publish event for container's subscribers
  if (_containerTask) 
    _containerTask->publish(newState);

  // State changed
  return True;
}


DeviceIF::Status SerialDeviceDriver::state()
{
  return _state;
}


DeviceIF::Status SerialDeviceDriver::readRecord(unsigned char *record,
						int maxRecordBytes,
						const char *recordTerminator,
						unsigned readTimeout,
						int *nBytesRead)
{
  Boolean debug = False;
  dprintf("SerialDeviceDriver::readRecord()");
  Boolean readError = False;

  try {

    // Read data record from device
    *nBytesRead = _device->readUntil((char *)record, maxRecordBytes, 
				     (char *)recordTerminator, 
				     readTimeout);
  }
  catch (SerialDevice::TimedOut) {
    readError = True;
    Syslog::write("%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() - caught exception:  abort", name());
    throw;
  }

  dprintf("SerialDeviceDriver::readRecord() - done");

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


DeviceIF::Status SerialDeviceDriver::beginCycle()
{
  Boolean debug = False;
  dprintf("SerialDeviceDriver::beginCycle()");
  return DeviceIF::Ok;
}


DeviceIF::Status SerialDeviceDriver::processCommand()
{
  Boolean debug = False;
  dprintf("SerialDeviceDriver::processCommand()");
  return DeviceIF::Ok;
}



DeviceIF::Status SerialDeviceDriver::requestData()
{
  Syslog::write("SerialDeviceDriver::requestData() - "
		"not implemented for device driver \"%s\"", 
		name());

  return DeviceIF::Error;
}


void SerialDeviceDriver::setContainer(Task *task)
{
  _containerTask = task;
}

