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

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

/*
CLASS 
SerialDeviceDriver

DESCRIPTION
Base class for 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. SerialDeviceDriver contains a state machine; the state is 
(in part) determined by the values returned by initialize() and 
processRecord(). When the state changes, SerialDeviceDriver triggers
an event indicating the new state value.

To use SerialDeviceDriver, an instance should be passed to the
constructor of one of the driver class objects, such as 
PolledDriverTask or StreamDriverTask.

AUTHOR
Tom O'Reilly
*/

class SerialDeviceDriver {

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
  SerialDeviceDriver(const char *name, 
		     SerialDevice *serialDevice,
		     unsigned maxRecordBytes,
		     const char *recordTerminator,
		     unsigned readTimeout, 
		     unsigned maxInitTries = 2); 


  ////////////////////////////////////////////////////////////////////
  // Destructor
  virtual ~SerialDeviceDriver();

  const char *name() {
    return _name;
  }

  ////////////////////////////////////////////////////////////////////
  // Try to initialize
  DeviceIF::Status startup();

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

  ////////////////////////////////////////////////////////////////////
  // Request a data packet from the device. By default this method 
  // exits. Polled devices should override.
  virtual DeviceIF::Status requestData();

  ////////////////////////////////////////////////////////////////////
  // Read and process device data
  virtual DeviceIF::Status acquire();

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


  ////////////////////////////////////////////////////////////////////
  // Specify container Task object
  void setContainer(Task *task);

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

  SerialDevice *_device;


private:

  // Read timeout in millisec
  unsigned _readTimeout;

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

  DeviceIF::Status _state;

  unsigned _maxInitTries;
  const char *_name;

  Task *_containerTask;
};


#endif
