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

#include "Task.h"
#include "SerialDevice.h"
#include "DeviceIF.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
*/

class StreamSerialDriver : public Task {

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 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();

  ////////////////////////////////////////////////////////////////////
  // Called on timeout while reading device. Does nothing by default.
  virtual void handleReadTimeout();


protected:

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

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

  ////////////////////////////////////////////////////////////////////
  // Polling timeout in millisec
  void setReadTimeout(unsigned millisec);

  ////////////////////////////////////////////////////////////////////
  // 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();

  SerialDevice *_device;


private:

  // Read timeout in millisec
  unsigned _readTimeout;

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

  DeviceIF::Status _state;

  unsigned _maxInitTries;
};


#endif
