//=========================================================================
// Summary  : */
// Filename : StreamSerialDriver.h
// Author   : */
// Project  : */
// Revision : 1
// Created  : 2000/08/19
// Modified : 2000/08/19
//=========================================================================
// Description :
//=========================================================================
#ifndef _STREAMSERIALDRIVER_H
#define _STREAMSERIALDRIVER_H

#include "Task.h"
#include "SerialDevice.h"
#include "DeviceIF.h"
#include "DeviceDriver.h"

/*

CLASS 
StreamSerialDriver

DESCRIPTION
Base class for streaming serial device drivers. Contains following
pure virtual methods:

  initialize() - initialize device

  processRecord() - process byte "record" 

Subclass implementations of these methods must return a DeviceIF::Status
value. StreamSerialDriver contains a state machine; the state is 
(in part) determined by the values returned by initialize() and 
processRecord(). When the state changes, StreamSerialDriver triggers
an event having the new state value.

AUTHOR
Tom O'Reilly

 FURTHER NOTES: all children must:

 - createCommand(&_command,"name")
 -  setSerialDevice(_device);

*/

class StreamSerialDriver : public DeviceDriver {

public:

  ////////////////////////////////////////////////////////////////////
  // Constructor
  // [input] name: Driver name
  // [input] serialDevice: Associated SerialDevice
  // [input] maxRecordBytes: Maximum bytes in data record
  // [input] recordTerminator: Data record terminator string
  // [input] readTimeout: Timeout (secs) on device read
  // [input] maxInitTries: Max number of initialization attempts
  StreamSerialDriver(const char *name, 
		     SerialDevice *serialDevice,
		     unsigned maxRecordBytes,
		     const char *recordTerminator,
		     unsigned readTimeout, 
		     unsigned maxInitTries = 2); 


  ////////////////////////////////////////////////////////////////////
  // Destructor
  ~StreamSerialDriver();

  ///////////////////////////////////////////////////////////////////////
  // Set allowable number of timeouts
  void setTimeOutLimit(unsigned limit) 
      { _timeOutLimit = limit; }; 

  ////////////////////////////////////////////////////////////////////
  // Set read timeout in millisec
  void setReadTimeout(unsigned timeout);

  ////////////////////////////////////////////////////////////////////
  // Read and process device data
  virtual void run();

  ////////////////////////////////////////////////////////////////////
  // Initialize device. Returned DeviceIF::Status (as determined by
  // subclass implementation) will be propagated to subscribers in 
  // other tasks.
  virtual DeviceIF::Status initialize() = 0;

  ////////////////////////////////////////////////////////////////////
  // Called on each cycle of run(). By default this method
  // does nothing; subclasses can override.
  virtual DeviceIF::Status beginCycle();

  ////////////////////////////////////////////////////////////////////
  // Read a data record from the device. By default this method
  // invokes SerialDevice::readUntil(); subclasses can override.
  virtual DeviceIF::Status readRecord(unsigned char *record, 
				      int maxRecordBytes, 
				      const char *recordTerminator,
				      unsigned readTimeout,
				      int *nBytesRead);

  ////////////////////////////////////////////////////////////////////
  // Process device data record. Returned DeviceIF::Status (as 
  // determined by subclass implementation) will be propagated to 
  // subscribers in other tasks.
  // [input] record: Record from device
  // [input] nRecordBytes: Bytes in record
  virtual DeviceIF::Status processRecord(unsigned char *record, 
					 int nRecordBytes) = 0;


  ////////////////////////////////////////////////////////////////////
  // Check for a command from another task (e.g., server), then 
  // process. By default, this method does nothing; subclasses 
  // can override.
  //  virtual DeviceIF::Status processCommand();

  ////////////////////////////////////////////////////////////////////
// Turn on/off messages on various readRecord exceptions
void verbose(void);
void silent(void);

  ////////////////////////////////////////////////////////////////////
  // Set the serial device
  void setSerialDevice( SerialDevice *newDev )
{ _device = newDev; };

protected:

  ////////////////////////////////////////////////////////////////////
  // Data record terminator
  const char *recordTerminator();

  ////////////////////////////////////////////////////////////////////
  // Polling timeout in millisec
  unsigned readTimeout();

  ////////////////////////////////////////////////////////////////////
  // Set state to newState, and trigger appropriate event. If newState
  // differs from current state, then trigger event, set current state
  // to newState, and return True. Else just return False.
  Boolean setState(DeviceIF::Status newState);

  ////////////////////////////////////////////////////////////////////
  // Return current state
  DeviceIF::Status state();

  ////////////////////////////////////////////////////////////////////
  // Functions to call on an error condition
  virtual void onError();
  virtual void onBufferFull();
  virtual void onException()
        { ; };

  //////////////////////////////////////////////////////////////////////
  //Pure virtual functions to make drivers deal with failures
  //note that the only reason these aren't pure virtual now
  //is that I didn't want to do right by every single gps
  virtual void errorState(){};
  virtual void failureState(){};
  virtual void onFailedInit(){};

  SerialDevice *_device;

private:

  Boolean _showTimedOut, _showBufferFull, _showMiscException;

  // Read timeout in millisec
  unsigned _readTimeout;

  unsigned _timeOutLimit;
  unsigned _timeOutCount;

  const char *_recordTerminator;
  unsigned char *_record;
  unsigned _maxRecordBytes;

  DeviceIF::Status _state;

  unsigned _maxInitTries;
};


#endif
