// This header file is used only by the Cocoon documentation utility.
/* ------------------------------------------------------------------
LIBRARY
AUV

OVERVIEW
The <i>AUV</i> library provides a framework for 
<i>Dorado/Odyssey III</i> onboard software applications.

An introduction to the design of the vehicle software is 
<a href="http://mww.mbari.org/~oreilly/auv/onboard/pdr/index.htm">
here</a>. The critical design review presentation of the software is 
<a href="http://mww.mbari.org/~oreilly/auv/onboard/cdr/index.htm">
here</a>.

The core onboard software consists of several cooperating <i>Tasks</i>.
Each Task has at least one dedicated thread-of-control, and carries 
out a particular system function (e.g. Navigation, LayeredControl, etc).

Tasks can make requests to - and receive responses from - other Task
objects. The requesting Task (the "client") simply instantiates a 
TaskInterface object of the appropriate subclass, and invokes its methods. 
The TaskInterface forwards the method invocation to the appropriate
"server" process and receives the response, hiding the details and 
complexities of intertask communication from the client code. 
This "distributed object" interface is similar to CORBA.

For example, the DynamicControl task communicates with the
Navigation task by instantiating a NavigationIF object, which
is a subclass of TaskInterface. NavigationIF provides methods
for retrieving data such as heading, location, depth, etc. 
When the client invokes one of these methods, NavigationIF 
copies the input arguments into a message buffer, and passes 
the message to the NavigationServer. The server then retrieves the
requested navigation data, copies it into the output field(s)
of the message, and passes the message back to NavigationIF and
the client. These "under-the-hood" details are not visible to 
the client code.


USAGE
The application developer implements a Task in several steps:

1) First, the public interface to the Task is specified using
a subset of Interface Definition Language (IDL). This subset,
which we refer to as "Mini IDL" or MIDL, specifies data types
and methods which clients can refer to when interacting with
the Task. The user's guide for MIDL and <i>idlToC++</i> (the
MIDL compiler) is 
<a href="http://mww.mbari.org/~oreilly/auv/idl/index.html">
here.
</a>
<img src="step1.gif">

2) Next, the MIDL interface is "compiled", using <i>idlToC++</i>. 
<i>idlToC++</i> currently runs on HPUX; the C++ files it generates are 
then compiled with the QNX C++ compiler.
<i>idlToC++</i> generates four files for each interface. For example, 
suppose we define a MIDL file called "MyInterface.idl". The
compiler generates the following files:

MyInterface.h
MyInterface.cc
These define a subclass of TaskInterface called "MyInterface". 
These files should be compiled with the QNX C++ compiler.
A client which interfaces with MyInterface includes MyInterface.h,
links with MyInterface.o, and instantiates a MyInterface object; the 
client can then invoke any of the MyInterface public methods.

MyInterface_SK.h
MyInterface_SK.cc
These define a "skeleton" subclass of TaskServer called "MyInterface_SK". This 
subclass includes pure virtual methods corresponding to the methods 
defined in MyInterface.idl. Note that these methods have exactly
the same signature as those declared in MyInterface.h. Since these 
methods are pure  virtual, the developer must subclass MyInterface_SK and 
supply "implementations" of these methods. 
<br>
<img src="step2.gif">

3) Subclass the skeleton class to a "server class", implementing the 
pure virtual interface methods. 
<br>
<img src="step3.gif">
<img src="step3a.gif">

4) Write a server that instantiates the server class and invokes its
<i>run()</i> method.
<br>
<img src="step4.gif">

5) Instantiate the interface class in your client code and invoke
its public methods.
<br>
<img src="step5.gif">


DEVICE DRIVERS
A device driver is a software component which provides an interface to 
control and retrieve data from a device, such as a sensor or actuator. 

Most AUV devices communicate with the main vehicle computer via a
serial port. The latency incurred during communication via the port
varies, depending on the specifics of the device. 

Other system components often need to send commands or retrieve data
from the device driver. In many cases it is desirable that the
latency incurred by these other components is minimized or at least
predictable. We can achieve this goal with a <i>multi-threaded</i>
device driver architecture. The driver application is divided into two 
cooperating processes. The <i>server</i> process receives and 
processes requests from other system components (<i>clients</i>).
The <i>driver</i> process directly accesses the serial port, writing
commands and reading data from the device.

The server and driver communicate via the TaskServer subscribe/notify 
mechanism, and the SharedData shared memory objects.

The objects and messages for this architecture are shown in figure 1.
<br>
<img src="img001.gif">
<br>
<b>Figure 1.</b>

The <i>device server</i> is a subclass of TaskServer, as shown in figure 2.
The <i>device server</i> instantiates <i>driver input</i> and a 
<i>driver output</i> objects, which provide data-sharing (via shared memory) 
with the driver process.
<br>
<img src="img002.gif">
<br>
<b>Figure 2.</b>

The <i>device driver</i> is a subclass of PeriodicTask, as shown in figure 2.
Like <i>device server</i>, the <i>device driver</i> also instantiates 
<i>driver input</i> and <i>driver output</i> objects; this enables 
data-sharing with the server. The <i>device driver</i> instantiates a 
TaskInterface to the server and subscribes to "new command" events. 
Finally, <i>device driver</i> defines a callback which
periodically reads data from the serial port and writes it to the
<i>driver output</i> object, making the data available to the server.

The class hierarchy for <i>driver input</i> and <i>driver output</i> is shown 
in figure 3.
<br>
<img src="img003.gif">
<br>
<b>Figure 3.</b>

A state transition diagram for the server is shown in figure 4.
When the server receives a client request which requires that a command
be sent to the device, it writes the appropriate command parameters
to the <i>driver input</i> object, then triggers an appropriate
notification event. The server then waits for further client requests. 
If the client request requires data from the device, the server reads
the <i>driver output</i> object and sends the data back to the client.

<br>
<img src="img004.gif">
<br>
<b>Figure 4.</b>

The state diagram for the driver is shown in figure 5.
After subscribing to server events and installing the periodic
callback, the driver pends on events. If the received event is
a notification from the server that a new command has been requested,
the driver reads command parameters from the <i>driver input</i> object, and
writes an appropriate command to the SerialDevice. If the received 
event is a timer expiration, the driver reads data from the SerialDevice,
then writes the data to the <i>driver output</i> object, making it available
to the server.

<br>
<img src="img005.gif">
<br>
<b>Figure 5.</b>

Note that the latencies experienced by the server are associated with
accessing the <i>driver input</i> and <i>driver output</i> objects 
(shared-memory protected with a semaphore) and triggering of new command 
events (implemented with QNX <i>proxies</i>). Thus, the server and its 
clients are not directly affected by the serial I/O latencies. Note however 
that the device data contained in <i>driver output</i> is only as 
up-to-date as the most recently-completed call to the SerialDevice read() 
method, so the "age" of the device data <b>is</b> subject to I/O latency.

Follow these steps to implement this design:

1. Subclass the <i>driver input</i> and <i>driver output</i> classes from 
SharedData. You can use the <i>mkSharedData</i> script to automatically 
generate most of the code; you then "fill in" the shared memory's data 
structure definition.

2. Define the server's public interface, using IDL. The IDL defintion
should include enumerated event codes for the various commands. Compile the
the IDL interface with <i>idlToC++</i>.

3. Subclass the "server skeleton" class generated by the IDL compiler. 
Your subclass should instantiate and contain a <i>driver input</i> and a 
<i>driver output</i> object. Implement the pure virtual methods inherited 
from the skeleton class, accessing the <i>driver input</i> and 
<i>driver output</i> objects, and triggering the new command events where 
appropriate (using TaskServer::triggerEvent()).
Note: for a given command, the server must only trigger the command event 
<b>after</b> it has written the command parameters to the DeviceInput.
Finally, override TaskServer::spawnAuxTasks() to fork and exec your
driver program (see below).

4. Write a main() function that instantiates your device server class and
invokes its TaskServer::run() method. 

5. Subclass your device driver class from PeriodicTask. Your class
should instantiate a SerialDevice object. Your class should define a callback()
which reads device data from the SerialDevice and writes it to the
<i>driver output</i> object; your class constructor
should register this callback with PeriodicTask::addPeriodicCallback().
Your class constructor should also create an interface to the server, and
subscribe to all of the server's "new command" events through the 
EventService object contained in PeriodicTask. The
callbacks associated with each command event should read the command parameters
from <i>driver input</i> and write an appropriate command message to the 
SerialDevice object.

6. Write a main() function that instantiates your device driver class and
invokes its PeriodicTask::run() method. This is the program that should be
exec'ed by your server's spawnAuxTasks() method.



DATA LOGGING
The DataLogWriter class provides a standard interface for logging data
to a file, in either binary or ASCII format. You can subclass DataLogWriter
to handle the logging needs of your application. 

A DataLogWriter contains a list of DataField objects. DataField
is a wrapper for primitive data types; DataField subclasses include
ShortData, IntegerData, FloatData, and others. When the DataLogWriter
<i>write()</i> method is called by an application, the value of each 
DataField is set by the setFields() method, and then the DataField values 
are written to a <i>logfile</i>. Each record in the logfile is automatically 
time-tagged.

Your subclass of DataLogWriter should be implemented as follows:

Subclass should contain appropriate DataField objects, one for each
data item to be logged. In your constructor, create each DataField and add 
it with DataLogWriter::addField(). Subclass should also contain pointer
to the <i>logged object</i>, which provides the data to be logged. 

For example, here is the definition for the NavigationLog class, which 
contains a pointer to a Navigation object:
<pre>

  class NavigationLog : public DataLogWriter {

  public:

    ///////////////////////////////////////////////////////////////////
    // Constructor
    NavigationLog(Navigation *navigation,        // setFields() method 
                                                 // accesses this

                  DataLog::FileFormat fileFormat // Either Binary or ASCII
                 ); 

    ~NavigationLog();

  protected:

    ///////////////////////////////////////////////////////////////////
    // Set values in DataFields. This virtual method is called
    // by DataLogWriter::write()
    virtual void setFields();

    ///////////////////////////////////////////////////////////////////
    // setFields() accesses the data to be logged from this Navigation 
    // object.
    Navigation *_navigation;


  private:

    AngleData *_heading;
    DoubleData *_x;
    DoubleData *_y;
    DoubleData *_z;
  };

</pre>

The constructor for NavigationLog is as follows:
<pre>

NavigationLog::NavigationLog(Navigation *navigation, 
			     DataLog::FileFormat fileFormat) 
  : DataLogWriter(NavigationLogName, fileFormat, AutoTimeStamp) 
{
  // Create and add DataFields
  addField((_heading = new AngleData("heading")));
  addField((_x = new DoubleData("x")));
  addField((_y = new DoubleData("y")));
  addField((_z = new DoubleData("z")));

  // This is the logged object
  _navigation = navigation;
}


</pre>

Your subclass must override DataLogWriter::setFields(). This method
should set the value of each DataField object, based on values provided
by the <i>logged object</i> passed in to the constructor. For example,
here is an implementation for NavigationLog:
<pre>
  void NavigationLog::setFields()
  {
    // Set heading value
    _heading->setValue(_navigation->_state.heading);

    // Set x, y, and z values
    _x->setValue(_navigation->_state.position.x);
    _y->setValue(_navigation->_state.position.y);
    _z->setValue(_navigation->_state.position.z);
  }

</pre>

To use your subclass, your application instantiates it, then periodically
calls its write() method inherited from DataLogWriter:
<pre>

  // Create logger, passing in pointer to this application object
  NavigationLog *log = new NavigationLog(this, DataLog::BinaryFormat);

  // [... other stuff happens here]

  // Write data to the logfile
  log->write();

</pre>

Environmental note: When running your application, environment variable 
AUV_LOG_DIR must be set to a directory which will contain the actual logfiles.
<p>
Several utilities display logfile contents:
<p>
<i>readlog</i>: Reads a logfile and displays the contents on stdout.
<p>
<i>logToMatlab</i>: Reads a logfile and displays contents on stdout,
in a format compatible with MatLab.


UTILITY CLASSES
Following are classes which are generally useful:
<pre>
Attribute
DataField
DynamicArray
Exception 
EventService
ExternalData
Math
Semaphore
StringConverter
Syslog
Time
</pre>

REFERENCES
<a href="../pdr/index.htm">Preliminary Software Design Review</a>
<br>
<a href="../cdr/index.htm">Critical Software Design Review</a>


------------------------------------------------------------------ */
