/****************************************************************************/
/* Copyright (c) 2000 MBARI                                                 */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
/* Summary  :                                                               */
/* Filename : SerialDevice.h                                                */
/* Author   :                                                               */
/* Project  :                                                               */
/* Version  : 1.0                                                           */
/* Created  : 02/07/2000                                                    */
/* Modified :                                                               */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/****************************************************************************/
#ifndef _SERIALDEVICE_H
#define _SERIALDEVICE_H


#include "DeviceInterface.h"

#include <stdio.h>
#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 <limits.h>

#include "DeviceInterface.h"
#include "Exception.h"
#include "SerialParameters.h"

#define ERROR -1
#define OK     0

// Easiest way to define "NoTimeout", although of course timeout WILL
// occur after INT_MAX (2147483647) seconds.
#define NoTimeout (INT_MAX)

/*
CLASS 
SerialDevice

DESCRIPTION
SerialDevice DeviceInterface performs I/O through a serial"port"

AUTHOR
Andrew Pearce

*/

class SerialDevice : public DeviceInterface
{
public:

  class TimedOut : public Exception {

  public:

    TimedOut(int nBytesRead);

    int nBytesRead();

  protected: 

    // Number of bytes read before timeout occurred
    int _nBytes;
  };
   

  class BufferFull : public Exception {

  public:

    BufferFull();

  };

  class BadSystemCall : public Exception {

  public:

    BadSystemCall(char *systemCall);

  };


  ///////////////////////////////////////////////////////////////////
  // Constructor
  // [input] ttyName: /dev/name for serial device
  SerialDevice(const char *ttyName);


  ///////////////////////////////////////////////////////////////////
  // Constructor
  // [input] params: SerialParameters for device
  SerialDevice(SerialParameters *params);

  virtual ~SerialDevice();

  ///////////////////////////////////////////////////////////////////
  // getFd - Returns file descriptor for tty device
  int getFd();

  ///////////////////////////////////////////////////////////////////
  // devName - Returns device name for tty device
  const char* devName();

  ///////////////////////////////////////////////////////////////////
  // nonBlocking - Sets device to non-blocking mode
  int nonBlocking();

  ///////////////////////////////////////////////////////////////////
  // blocking - Sets device to blocking mode
  int blocking();

  ///////////////////////////////////////////////////////////////////
  // raw - Sets device to a "raw" input mode
  int raw();
  int raw(int vmin, int vtime);

  ///////////////////////////////////////////////////////////////////
  // nonRaw - Sets device to a "cooked" input mode
  int nonRaw();

  ///////////////////////////////////////////////////////////////////
  // sendBreak - Sends a break to the device
  int sendBreak();

  ///////////////////////////////////////////////////////////////////
  // sendBreak - Send break of user specified duration to device
  int sendBreak(int milliseconds);

  ///////////////////////////////////////////////////////////////////
  // read - reads chars from port into the string reply until a
  // termination character is received or until timeout (in millisecs)
  // elapses. A timeout value of 0 performs a blocking read 
  // assumes memory already allocated for reply.
  // log diagnostics if enable by CommDebugMode
  virtual int readNChars( char *buf, int count, int timeout );

  virtual int read(char *buf, int maxBytes, int timeout)
    throw (TimedOut, BadSystemCall);


  ///////////////////////////////////////////////////////////////////
  // writeData - clear read buffer, then send message to port,
  // log diagnostics if enable by CommDebugMode

  virtual int write(const char *buf, int nBytes);

  ///////////////////////////////////////////////////////////////////
  // Reset device
  virtual void reset();

  enum CommsDebugMode {
    DebugOn, DebugOff
  };

  ///////////////////////////////////////////////////////////////////
  // commsDebugMode - Sets Communications Debug Mode (Default is off)
  void commsDebugMode(CommsDebugMode mode);

  ///////////////////////////////////////////////////////////////////
  struct lineFormat {
       speed_t baud;
       int data, stop;
       const char *parity;
  };

  ///////////////////////////////////////////////////////////////////
  // Set line parameters using SerialParameters object
  int setLineFormat(SerialParameters *params);

  ///////////////////////////////////////////////////////////////////
  // SetLineFormat
  // [input] baud: baud rate
  // [input] data: data bits
  // [input] stop: stop bits (1 or 2)
  // [input] parity: parity setting (NONE, EVEN, ODD)
  int setLineFormat(speed_t baud, int data, int stop, const char *parity);
  inline int setLineFormat( lineFormat *inFmt ) 
    { return setLineFormat( inFmt->baud, inFmt->data, 
			    inFmt->stop, inFmt->parity ); };

  ///////////////////////////////////////////////////////////////////
  // nRecvdBytes - Returns number of bytes in the receive buffer
  int nRecvdBytes();

  void clearPort();

  ///////////////////////////////////////////////////////////////////
  // flushReceiveBuf - checks to see if there are any chars to read from port,
  // and if so it reads and discards them.
  int flushReceiveBuf();

  ///////////////////////////////////////////////////////////////////
  // reads characters into buffer until string is received from device
  int readUntil(char *buf, int maxBytes, char *term, int timeout) 
    throw (TimedOut, BufferFull, BadSystemCall);

  ///////////////////////////////////////////////////////////////////
  // Confirm that a string was received from device
  int readUntil(char *term, int timeout)
    throw (TimedOut, BadSystemCall);

  ///////////////////////////////////////////////////////////////////
  // Confirm that a string was received from device
  int confirm(char *term, int timeout);

  void clearPort();

  void printTermios();

  int writeRepeat( const char *string, int nBytes, int reps );

  Boolean isRaw();

protected:

  void initialize(const char *ttyName);

  int NonBlocking();
  int Blocking();
  int sendBreak();

  // Timeout-test, via select() system call
  void selectTimeout(int timeoutMillisec);

  Boolean debugging();

private:

  FILE *_dev;
  int _fd;

  struct termios orig_termios_p;

  CommsDebugMode  _commsDebugMode;

  Boolean _raw;
  Boolean _isSocket;
  int csetStopBits(struct termios *termios_p, int stopBits );
  int csetDataBits(struct termios *termios_p, int dataBits );
  int csetParityBits(struct termios *termios_p, const char *parity);
};

#endif


