Outboard modules:
================

For detailed information about outboard modules, please refer to 
the Data Explorer Programmer's Reference, "Compiling, 
Linking, and Debugging an Outboard Module."

In this directory you will find a number of sample outboard modules.
Copy the appropriate Makefile_architecturename to Makefile.

hello.c is an example of the hello module. Use "make hello" to make
an executable. Use "dx -mdf hello.mdf" to start dx using it.

async.c is a simple asynchronous module (one which can initiate
an execution). Use "make async" to make an executable. Use
"dx -mdf async.mdf" to start dx using it.

watchfile.c is a module which waits for a file to change.
When the file changes, a new execution is triggered, and
the data in the file is reimported. Use "make watchfile" to 
make an executable. Use "dx -mdf watchfile.mdf"
to start dx using it. 

watchsocket.c is a module which waits for input over a socket,
for example from a simulation program.  When input appears, a 
new execution is triggered. Use "make watchsocket" to make 
an executable. Use "dx -mdf watchsocket.mdf"
to start dx using it. A sample net, watchsocket.net, uses
this module.



 
The basic steps to make an outboard module are:
 
1) Decide on the interface to the module:  the inputs, the outputs and
the module name.  Make an .mdf file which contains this information.
It will be used at run time to describe your module to the system.  For
examples of .mdf entries, see the section on adding modules in the
programmers guide, see the examples in this directory, and look in
$DXROOT/lib/dx.mdf (normally lib/dx.mdf) for the mdf entries
for all the modules which come with DX.
 
2) Using the sample makefiles and modules in this directory as templates,
write your outboard module and compile it into a separate executable
program.
 
3) Start dx with the additional flag `-mdf yourmodule.mdf' when running
with the user interface.  When running in script mode, the -mdf flag is
not processed so you must add the commands: 
  Executive("mdf file", "yourmodule.mdf");
  $sync
to your script before calling your module.
 
4) To run your outboard module under a debugger, start dx with this
additional flag `-outboarddebug'.  Instead of automatically starting 
your outboard module, DX will prompt you to start your outboard by hand.  
You can then start and run the module from the debugger.
 
5) To change the interface to an outboard module or to add the mdf entry
for an outboard module if you forgot to use the -mdf flag at startup,
from the User Interface you can use the "Load Module Description File"
option from the File menu.  From script mode, you can rerun the Executive
command as described above.
 
 
Special Consideration for Different Types of Outboards:
======================================================
 
Simple outboard modules:
------------------------
 
The simplest type of outboard doesn't need to save any information,
doesn't communicate with any other process, and doesn't cause executions
at asynchronous times.  It takes inputs, computes something based on
them, and returns outputs.  The executable program which makes up the
outboard module is run each time the module is called and exits after
it returns the output values.
 
Persistent outboard modules:
---------------------------
 
If the executable program which makes up the outboard module should not
exit after each execution then the PERSISTENT flag should be set on the
FLAGS line in the .mdf file.  Modules might need to do this so they can
save information from one execution to another, or because the time it
takes them to start up is long and it would be too slow to start and stop
a new process during every execution of the network.
 
Using the User Interface, a persistent outboard module is started the
first time the module is called and does not exit until the box
representing that module is deleted from the network (or the entire
network is deleted), or by selecting the "Reset Server" option from the
"Connection" menu.
 
Using script mode, persistent outboard modules are loaded the first
time the module is called and they do not exit until the executive
exits or Executive("flush dictionary"); is run.
 
Global variables can be safely be used to save information between
executions of the module.  If the same outboard module occurs in a
network more than once, a different process is started for each instance.
 
Note that the module is not guaranteed to be called during every
execution.  If the inputs to the module are changed from one value
to another and then back to the original value, the previous
results are saved by DX and are used without calling the module
again.  You can use the "cache none" option on the module to
prevent previous execution results from being saved, but you may
have to set "cache none" on all subsequent modules which process
the outputs of the module or the caching at the lower levels will
still prevent the module from being called each time.  The flag
SIDE_EFFECTS will guarantee that the module is called during every
execution, with the performance penalty of it running even if the
inputs are the same as a previous run.
 
Modules which can cause executions:
----------------------------------
 
A module can request that it be rerun the next time the network is
executed.  If DX is in execute-on-change mode this will cause a new
execution.  If it is not in execute-on-change module, then the module
will be called the next time the user runs the network.
 
To be able to cause executions, a module must have the the ASYNC
flag set on the FLAGS line in its .mdf file.  Then it can call the
DXReadyToRun() function to request to be rerun.  See the sample
outboard files "async.c", "watchfile.c", and "watchsocket.c"
in this directory for examples of how to use this function.  
The PERSISTENT flag will also need to be set so the module doesn't 
exit after every execution.  DXReadyToRun() can be called from a 
variety of places:  by a signal handler (signal), after a prescribed 
time interval has passed (sleep, alarm), when a file appears (stat) or 
when data is received across a pipe or socket from another process 
(tracking a simulation).
 
If an outboard module wants to wait for information from another file
descriptor it should use DXRegisterInputHandler to add the file
descriptor to the select list, specifying a worker routine to be
called when data appears.  The worker routine then calls 
DXReadyToRun which reruns the outboard module. For an  
example, see watchsocket.c in this directory.
 
 
Running an outboard on another machine:
======================================
 
If an outboard module should be run on only one particular machine
(perhaps because it is compute intensive and needs to run on a fast
machine, or because it needs to access a peripheral which is
connected to only one machine), the OUTBOARD line can specify a host
name as well as an executable name.  The dx libraries will take care
of establishing a connection between where the main dx executive is
running and the outboard host machine.  The DXMODULES environment
variable or the -modules flag can be used to specify a search path
for outboard modules executable files, or the OUTBOARD line can
specify a fully qualified path name.
 
A valid .rhosts file must be present to allow DX to use the
"rsh" command to start a process on another machine.  See the
unix manual page for "rsh" or "remsh" for more information.
 
 
Miscellaneous information about outboard modules:
================================================
 
DXReadyToRun cannot be called between the time a module receives
its inputs and when it returns its outputs.  If a module wants to
immediately trigger another execution it can do it after the call
to DXCallOutboard() in the outboard.c file.
 
Outboard modules cannot be written in coroutine style.  They cannot
produce outputs without being called by DX and thereby receiving new
inputs (which can be ignored), and they must return something - the main
dx executive will wait for the module to return before continuing.
 
When running in script mode, the -mdf command line flag is not recognized
so you must use the `Executive("mdf file", "yourmodule.mdf");' command.
This must be followed by `$sync' before actually calling the module to
ensure that the module definition completes before trying to use it.  
 
An asynchronous module cannot be run in distributed mode, but it can be
executed on another machine by setting the hostname on the OUTBOARD line.
 
