\subsubsection{The Tcl Environment Driver}
Sometimes a quick way for defining an environment
device driver is needed. A suitable way for doing this 
is to define the device driver in the SICS servers Tcl macro language. Then
a device driver can even be developed and tested offline from a psish before
inclusion in the server. Clearly a means of communication between SICs and 
the actual device is needed. Usually this communication happens via 
a RS--232 serial port. Such a communication is catered for by the SICS
serialport command. This section describes the environment device driver
which links the SICS C code with Tcl. For each of the device drivers
functions a C language wrapper function is provided, which in turn calls
the Tcl interpreter with some parameters. This scheme is initialised by
providing the Tcl environment device driver with the name of a Tcl array
which has to hold Tcl implementations for all the necessary driver functions.
The name of this array will also be passed to each of the Tcl procedures as 
first parameter. The Tcl procedures to define for a Tcl driver are:
\begin{description}
\item[Init] to be called at initialisation of the driver. This Tcl procedure must connect
to the device and prepare it for accepting further commands. The parameters to
init are all the parameters still left in the command line after the name
of the Tcl array in the EVFactory command line.
\item[Close] should close down the connection to the device after bringing the
device into a clean state.
\item[SetValue] will be called with a float value as second parameter. It
should issue a command to set the  device to a new value. The return value is an
integer error code, defined by the driver. Error returns use the normal Tcl mechanism
with the Tcl error command. On errors a negative error code defined by the driver 
must be returned.
\item[GetValue] reads the current value from the device. The return value is
either the floating point value of the current control variable or the
string pending if the request is still pending. In case of failure an error
is returned with a negative integer error code. 
\item[Send] should concatenate all parameters and send them down to the 
device. Returns error codes as described above.
\item[GetError] gets as a parameter the negative error code possibly returned
from one of the calls above.  GetError should return a textual description
of the error.
\item[TryFixIt] gets as a parameter a negative error code as returned from one
of the calls described above. TryFixIt should do everything possible in
software to get the device working again: perhaps send some commands, 
reopen the connection or whatever apropriate. Depending on the success
of this TryFixIt returns either DEVREDO if the command shall be resend or
DEVFAIL if the problem cannot be fixed without human intervention.
\item[Wrapper] This function will will be called by the environment variable 
interpreter wrapper function before the general part of the wrapper
function gets executed. Its purpose is the handling of subcommands special
to the specific environment device. For instance modifying its mode of
operation or setting special variables not covered in the general
environment device setup. It has to return 1 on success, 0 on failure. On
failure it has to print error messages as well.  
\end{description}   

In order to do all this a new driver must be defined. This driver needs the
following data structure:

@d tclevdat @{
   typedef struct __TclEv {
                               char *pArray;
                               char *pInit;
                               char *pClose;
                               char *pSetValue;
                               char *pGetValue;
                               char *pSend;
                               char *pGetError;
                               char *pTryFixIt;
                               char *pWrapper;
                               char *pName;
                               Tcl_Interp *pTcl;
                               int iLastError;
                              } TclEv, *pTclEv;
@}

The fields correspond to the names of the Tcl functions to call for each of the
functions specified above.   Plus  the name of a Tcl array used for storing
driver special data in Tcl. iLastError will be used to keep the last error code.
pName is the name of the device in SICS. This will be copied into the
Tcl-array describing the device as MyName. May be used to set general
evcontroller parameters from within driver code and for error messages.

SICS sees this driver through the following interface:
@d tclevint @{
   pEVDriver CreateTclDriver(int argc, char *argv[],char *pName, SConnection *pCon);
   int UpdateTclVariable(pEVDriver self, char *name, float fVal);

   int TclEnvironmentWrapper(SConnection *pCon, SicsInterp *pSics, 
                             void *pData, int argc, char *argv[]);
@}
\begin{description}
\item[CreateTclDriver] creates the Tcl driver and initialises it.
\item[UpdateTclVariable] will be called from the general environment
variable parameter update code in order to allow updating of Tcl variables
as well.
\item[TclEnvironmentWrapper] is the interpreter  wrapper function for a Tcl
driver. 
\end{description}

@o tclev.h @{
/*---------------------------------------------------------------------------
                  T C L E N V I R O N M E N T

   This is the header file for an environment device driver defined in 
   the Tcl macro language. Dor more details see tclev.tex

   copyright: see copyright.h

   Mark Koennecke, February 1998
------------------------------------------------------------------------------*/
#ifndef SICSTCLEV
#define SICSTCLEV
  
@<tclevint@>  

#endif


@}
@o tclev.i @{
/*---------------------------------------------------------------------------
   Tcl environment device driver data structure definition file. 

   No general usable header file.

   Mark Koenencke, February 1998
----------------------------------------------------------------------------*/
@<tclevdat@>
@}
