Files
sics/doc/programmer/sicsdriver.tex
cvs 189f7563b6 - Fixed a bug at the new counter driver
SKIPPED:
	psi/el737hpdriv.c
	psi/el737hpv2driv.c
	psi/make_gen
	psi/psi.c
	psi/tas.c
	psi/tasdrive.c
	psi/tasinit.c
	psi/tasscan.c
	psi/tasutil.c
2003-08-08 07:30:40 +00:00

640 lines
32 KiB
TeX

\chapter{Writing SICS Device Drivers}
This chapter deals with writing new hardware drivers for SICS. SICS
hardware has a dual identity: Towards upper level code SICS hardware
is represented by the logical hardware object. All the low level
detail is handled in the hardware driver. The point of this is that
upper level code does not need to know which type of hardware device
is being accessed. Experience shows that this scheme covers most usage
cases for a given hardware device. However, there were always
exceptions mostly in order to realize special configurations. Such
exceptions can be dealt with through special macros which implement
commands which do
special configuration tasks. In order to be able to write such scripts
it is feasible to organize hardware access into three layers:
\begin{itemize}
\item A communication layer. This layer allows for sending commands
and reading data along a given bus.
\item A controller layer. If a device or several share a controller
make the controller visible within the system. Allow for sending
suitable commands to it.
\item The actual SICS driver.
\end{itemize}
This organisation allows scripts to directly talk to devices through
either the controller or the communication layer. If something is
missing in the general driver interface it is usually easier to add
some code at controller or communications level, rather then change
all drivers present in the system.
All drivers use a common pattern for error handling. Please read the
section on the motor driver where this pattern is explained in more
detail. The same pattern is applied in most drivers.
Please be aware that the hardware drivers have a significant impact on
SICS's overall performance. Most of the time SICS will be sitting
there and wait for a counter or a motor to finish. And often the
devices controlling such things respond pretty slow. The most often
called function is the status check function of the driver. It is
advisable to optimize this is good as possible. Suggestions for this
include:
\begin{itemize}
\item Reduce the amount of data to be read from the controller as much
as possible.
\item Make the status function a state machine: in the first state a
status request command is sent and the second state is entered. In teh
second state, the function checks if data is available on the
communication port and process if this is so, else it returns the
appropriate busy code.
\end{itemize}
This section describes the actual drivers. How these drivers are
integrated into SICS is described in the chapter on the site data
structure (see \ref{site}).
\section{The Motor Driver}
A motor driver again is represented by an interface encapsulated in a
data structure. Polymorphy is achieved in two ways:
\begin{itemize}
\item Through the functions you have to define for your motor and to
assign to the function pointers in the motor driver data structure.
\item For the data structure, polymorphy is achieved through
overlay. This means, if you define your own motor driver data
structure the first fields up to KillPrivate MUST be the same as
defined in the MotorDriver structure defined below. You MUST append
your own fields below KillPrivate.
\end{itemize}
This is the motor driver data structure which has to be implemented:
\begin{verbatim}
typedef struct __AbstractMoDriv {
/* general motor driver interface
fields. REQUIRED!
*/
float fUpper; /* upper limit */
float fLower; /* lower limit */
char *name;
int (*GetPosition)(void *self, float *fPos);
int (*RunTo)(void *self,float fNewVal);
int (*GetStatus)(void *self);
void (*GetError)(void *self, int *iCode, char *buffer, int iBufLen);
int (*TryAndFixIt)(void *self, int iError,float fNew);
int (*Halt)(void *self);
int (*GetDriverPar)(void *self, char *name,
float *value);
int (*SetDriverPar)(void *self,SConnection *pCon,
char *name, float newValue);
void (*ListDriverPar)(void *self, char *motorName,
SConnection *pCon);
void (*KillPrivate)(void *self);
} MotorDriver;
\end{verbatim}
In order not to have to repeat trivial things all the time two general
things must be stated:
\begin{itemize}
\item The pointer self is always a pointer to the motor driver data
structure.
\item Functions return 1 on success or 0 on failure if not stated otherwise.
\end{itemize}
The elements of this data structure are:
\begin{description}
\item[fUpper,fLower] These are the motors hardware limits. These
values are supposed to be identical to the positions of the limit
switches on the real thing. Read them from the motor or initialize
them from parameters when initializing the motor driver.
\item[GetPosition] reads the position of the motor and puts the result
into fPos. This ought to be the position from the motor
controller. Software zeros are applied later by code in
motor.c. GetPosition returns either OKOK when the request succeeded
or HWFault on failure.
\item[RunTo] Starts the motor to run towards the new position
fNewVal. fNewVal must be a value valid for the controller. Software
zero points have already been taken account of by code in
motor.c. This function shall NOT wait for the completion of the
driving operation. RunTo returns either OKOK when the request succeeded
or HWFault on failure.
\item[GetStatus] This function is called repeatedly by upper level
code to poll for the progress of the driving operation. Possible
return values of this function are:
\begin{description}
\item[HWFault] If there is a fault in the hardware or the status
cannot be read.
\item[HWPosFault] The motor is still alive but the controller was
unable to position the motor.
\item[HWBusy] The motor is still driving.
\item[HWWarn] There is a warning from the controller.
\item[HWIdle] The motor has finished driving and is idle.
\end{description}
\item[GetError] retrieves information about an error which occurred on
the motor. An integer error code is returned in iCode. Up to iBufLen
characters of descriptive error information is copied into
buffer. This information is printed as error message by upper level
code.
\item[TryAndFixIt] Given an error code in iError, try to repair the
problem as far as this is possible in software. iError should be an
error code as returned by GetError in iCode. This function has the
following return codes:
\begin{description}
\item[MOTREDO] Problem fixed, try to redo the last the operation.
\item[MOTFAIL] The problem cannot be fixed in software.
\end{description}
The parameter fNew is the target position of the motor.
\item[Halt] stops the motor immediately.
\item[GetDriverPar] copies the value of the motor driver parameter
name into value, if such a parameter exists.
\item[SetDriverPar] sets the motor driver parameter name to
newValue. Report errors to pCon.
\item[ListDriverPar] write the names and values of all driver
parameters to the client connection pCon.
\item[KillPrivate] releases any memory possibly allocated for private
fields in the motor data structure.
\end{description}
In order to understand the relationship between GetError and
TryAndFixIt it helps to look at the way how errors are handled by
upper level code in motor.c: If an error in any function occurs,
GetError gets called. An error message is printed. Then TryAndFixIt is
called with the error code returned in iCode as a parameter. If
TryAndFixIt returns MOTFAIL, the code gives up. If TryAndFixIt
returns MOTREDO, the failed operation is retried. At max 3 retries are
performed. If the operation does not succeed after three
retries, a motor failure is reported.
The GetDriverPar, SetDriverPar and ListDriverPar functions implement
some support for driver private configuration parameters. Such
parameters are meant to be configured from the instrument
initialization file. Currently there is no support to include these
parameters into the status file. If there are
no such parameters have these functions do nothing and return 1.
\section{The Counter Driver}
A counter driver is a driver for some box which allows to count for a
preset time or monitor and manages single counters and monitors. Such
a driver is represented by a data structure:
\begin{verbatim}
typedef struct __COUNTER {
/* variables */
char *name;
char *type;
CounterMode eMode;
float fPreset;
float fLastCurrent;
float fTime;
int iNoOfMonitors;
long lCounts[MAXCOUNT];
int iPause;
int iErrorCode;
/* functions */
int (*GetStatus)(struct __COUNTER *self, float *fControl);
int (*Start)(struct __COUNTER *self);
int (*Pause)(struct __COUNTER *self);
int (*Continue)(struct __COUNTER *self);
int (*Halt)(struct __COUNTER *self);
int (*ReadValues)(struct __COUNTER *self);
int (*GetError)(struct __COUNTER *self, int *iCode,
char *error, int iErrLen);
int (*TryAndFixIt)(struct __COUNTER *self, int iCode);
int (*Set)(struct __COUNTER *self,char *name,
int iCter, float fVal);
int (*Get)(struct __COUNTER *self,char *name,
int iCter, float *fVal);
int (*Send)(struct __COUNTER *self, char *pText,
char *pReply, int iReplyLen);
void (*KillPrivate)(struct __COUNTER *self);
void *pData; /* counter specific data goes here, ONLY for
internal driver use!
*/
} CounterDriver, *pCounterDriver;
\end{verbatim}
Polymorphy is achieved through the function pointers. Differences in
the data structure for different counter boxes are accounted for
through the pData pointer. This is meant to be initialized by the
actual counter driver to a private data structure which holds data
relevant to this particular counter. All functions take a pointer to
this counter driver structure as parameter self. If not stated
otherwise functions return 1 on success and 0 on failure. The fields:
\begin{description}
\item[name] The counter name in SICS
\item[type] The driver type.
\item[eMode] The counter mode. Possible values eTimer for preset timer
and eMonitor for preset monitor operation. This mode will be set by
upper level code.
\item[fPreset] The preset for either timer or monitor.
\item[fLastCurrent] the last known value for the control variable
during counting. Gets updated in GetStatus while counting and is used
for reporting count status.
\item[fTime] The time the last counting operation took. This is a time
read from the counter box. This could be different from elapsed time
because the count may have paused for instance because the beam was
low.
\item[iNoOfMonitors] is the number of monitors and counters this
counter box supports.
\item[lCounts] An array for storing the values of counters and
monitors after counting. The PSI EL7373 counter box allows to read
values only once after counting finished. This is why the values had to be
cached in lCounts.
\item[iPause] A flag which becomes true if the counter has been
paused.
\item[iErrorCode] A private variable holding error codes.
\item[GetStatus] This function is called while upper
level code polls for the counter to finish. It has to return the
status of the counting operation and update the current value of the
control variable in fControl. Possible return values are:
\begin{description}
\item[HWBusy] when counting.
\item[HWIdle] when finished counting or idle.
\item[HWNoBeam] when counting is halted due to lack of beam.
\item[HWPause] if counting is paused.
\item[HWFault] if the status cannot be obtained.
\end{description}
\item[Start] start counting in the count mode and with the preset
previously confugured. Do NOT wait for counting to finish!
\item[Pause] pause counting.
\item[Continue] continue a paused counting operation.
\item[Halt] stop counting.
\item[ReadValues] read all counters and monitors into lCounts.
\item[GetError] retrieves information about an error which occurred on
the counter. An integer error code is returned in iCode. Up to iErrLen
characters of descriptive error information is copied into
error. This information is printed as error message by upper level
code.
\item[TryAndFixIt] Given an error code in iCode, try to repair the
problem as far as this is possible in software. iError should be an
error code as returned by GetError in iCode. This function has the
following return codes:
\begin{description}
\item[COREDO] Problem fixed, try to redo the last the operation.
\item[COTERM] The problem cannot be fixed in software.
\end{description}
\item[Set] set parameter name associated with counter iCter to fVal.
\item[Get] return in fVal the value of parameter name associated with
iCter. These two functions allow to set counter driver parameters.
\item[Send] send pText to the counter controller and return iReplyLen
characters of response from the counter controller in pReply. This is
a bypass to set controller parameters manually.
\item[KillPrivate] properly delete counter driver private data
pData. Also close any connections to the hardware.
\end{description}
\section{Environment Controller Driver}
This is the driver for all sample environment controllers, be it
temperature controllers, magnet controllers etc. An environment
controller driver is represented by the following data structure:
\begin{verbatim}
typedef struct __EVDriver {
int (*SetValue)(pEVDriver self, float fNew);
int (*GetValue)(pEVDriver self, float *fPos);
int (*GetValues)(pEVDriver self, float *fTarget,
float *fPos, float *fDelta);
int (*Send)(pEVDriver self, char *pCommand,
char *pReplyBuffer, int iReplBufLen);
int (*GetError)(pEVDriver self, int *iCode,
char *pError, int iErrLen);
int (*TryFixIt)(pEVDriver self, int iCode);
int (*Init)(pEVDriver self);
int (*Close)(pEVDriver self);
void *pPrivate;
void (*KillPrivate)(void *pData);
} EVDriver;
\end{verbatim}
All functions take a pointer to their own data structure as the first
argument (self). They return 1 on success or 0 on failure if not
stated otherwise.
The fields:
\begin{description}
\item[SetValue] set fNew as the new set value for the device. It
should start heating or cooling or whatever then.
\item[GetValue] reads the current value from the device into *fPos.
\item[GetValues] is used when the readout sensor and the control
sensor are very different. This function then reads the current set
value, the current position and calculates the difference between
these value into fDelta. This function does not need to be defined, it
is replaced by a standard one based on GetValue if not present.
\item[Send] send a command in pCommand to the controller and returns
at max iReplBuflen bytes of result in pReplyBuffer. This is breakout
which allows to send arbitray data to the controller.
\item[GetError] retrieves information about an error which occurred on
the device. An integer error code is returned in iCode. Up to iErrLen
characters of descriptive error information is copied into
pError. This information is printed as error message by upper level
code.
\item[TryAndFixIt] Given an error code in iError, try to repair the
problem as far as this is possible in software. iError should be an
error code as returned by GetError in iCode. This function has the
following return codes:
\begin{description}
\item[DEVREDO] Problem fixed, try to redo the last the operation.
\item[DEVFAULT] The problem cannot be fixed in software.
\end{description}
\item[Init] initializes a connection to a controller and puts the
thing into the right mode.
\item[Close] closes a connection to a controller.
\item[pPrivate] A pointer to a driver specific data structure which
can be filled with meaning by instances of drivers.
\item[KillPrivate] a function which has to release all memory associated
with pPrivate.
\end{description}
\section{Histogram Memory Drivers}
Histogram memories are devices in which neutron events for area
detector or time--of--flight detectors are assigned to their correct
bins. Then these usually large data sets have to be transferred to
SICS for further processing. In SICS, histogram memories are also able
to do count control, i.e. count until a preset monitor or time is
reached. This gives a slightly complicated driver interface. If this
assumption does not hold there are two options:
\begin{itemize}
\item Pass in a counter as a configuration parameter and chain count
control to this counter.
\item Make the count control functions dummies and let HMControl do
the rest. See hmcontrol.h and .c for details.
\end{itemize}
Though never used so far the histogram memory driver has support for
multiple banks of detectors being controlled by one histogram memory.
A histogram memory driver is implemented by filling in the data
structure given below:
\begin{verbatim}
typedef struct __HistDriver {
pHMdata data;
/* counting operations data */
CounterMode eCount;
float fCountPreset;
/* status flags */
int iReconfig;
int iUpdate;
/* interface functions */
int (*Configure)(pHistDriver self,
SConnection *pCon,
pStringDict pOpt,
SicsInterp *pSics);
int (*Start)(pHistDriver self,
SConnection *pCon);
int (*Halt)(pHistDriver self);
int (*GetCountStatus)(pHistDriver self,
SConnection *pCon);
int (*GetError)(pHistDriver self,
int *iCode,
char *perror,
int iErrlen);
int (*TryAndFixIt)(pHistDriver self,
int iCode);
int (*GetData)(pHistDriver self,
SConnection *pCon);
int (*GetHistogram)(pHistDriver self,
SConnection *pCon,
int i,
int iStart, int iEnd,
HistInt *pData);
int (*SetHistogram)(pHistDriver self,
SConnection *pCon,
int i,
int iStart, int iEnd,
HistInt *pData);
long (*GetMonitor)(pHistDriver self,
int i,
SConnection *pCon);
float (*GetTime)(pHistDriver self,
SConnection *pCon);
int (*Preset)(pHistDriver self,
SConnection *pCon,
HistInt iVal);
int (*Pause)(pHistDriver self,
SConnection *pCon);
int (*Continue)(pHistDriver self,
SConnection *pCon);
int (*FreePrivate)(pHistDriver self);
void *pPriv;
} HistDriver;
\end{verbatim}
All functions take a pointer to their driver data structure as an
argument. If not stated otherwise they return 1 on success, 0 on failure.
\begin{description}
\item[data] Is a pointer to an HMdata object which does all the
dimension handling, buffers histogram memory content, deals with time
binnings etc.
\item[eCount] A counter mode, as defined above for counters.
\item[fCountPreset] A preset for either monitor or time.
\item[iReconfig] A flag which will be set by upper level code when a
reconfiguration is necessary.
\item[iUpdate] a flag which invalidates the buffered histogram. Should
be set 1 in each call to GetCountStatus.
\item[Configure] configures the histogram memory to the specifications
given in the fields of the HMdriver structure. Further driver specific
information can be read from the options dictionary passed
in. Configuration options are stored in the string dictionary
pOpt. This dictionary holds name value pairs which must be interpreted
by this routine. Then configure has to configure the histogram memory
according to the options passed in.
\item[Start] starts a counting operation according to the current
settings of the counter mode parameters.
\item[Halt] implements an emergency stop of a counting operation.
\item[GetCountStatus] serves to monitor the status of the counting
operation. Possible return values to this call are:
\begin{itemize}
\item HWBUSY when still counting.
\item HWNoBeam when the monitor is to low.
\item HWIDLE or OKOK when nothing is going on.
\item HWFault when there is an error on the device.
\end{itemize}
\item[GetError] will be called whenever an error has been detected on
the device. The task is to put an internal error code into the iCode
parameter. The string parameter perror will be filled with a text description
of the error. But maximum iErrlen characters will be transferred to the error
string in order to protect against memory corruption. Therefore iLen must be
the maximum field length of error.
\item[TryAndFixIt] is the next function called in case of an error on
the device. Its second parameter is the internal code obtained in the ICode
parameter of the call to GetError. The task of this function is to examine
the error code and do whatever is possible in software to fix the problem.
TryAndFixIt returns one of the following values:
\begin{itemize}
\item COREDO when the error could be fixed, but the upper level code will
need to rerun the command which failed.
\item COTERM when the software is unable to fix the problem and a real
mechanic with a hammer is needed (or somebody able to reboot!).
\item MOTOK when the error was fixed and nor further action is necessary.
\end{itemize}
\item[GetData] transfers all the data collected in the HM into the
host computers memory buffer.
\item[GetHistogram] copies data between iStart and iEnd from histogram
bank i into the data space pData. Please make sure that pData is big
enough to hold the data.
\item[SetHistogram] presets the histogram bank i with the data
given in lData. A conversion from different binwidth
to long is performed as well. iStart and iStop define the start and end of
the stretch of histogram to replace.
\item[GetMonitor] returns the counts in the monitor i. GetMonitor returns a
negative value on error. The error will have been printed to pCon.
\item[GetTime] returns the actual counting time.
\item[Preset] initializes the histogram memory to the value given by
iVal.
\item[Pause] pauses data collection.
\item[Continue] continues a paused data collection.
\item[FreePrivate] will be called automatically by DeleteHistDriver and
has the task to remove the private data installed by implementations of an
actual histogram memory driver.
\item[pPriv] is a pointer which a actual histogram memory driver may
use to hold a driver specific data structure.
\end{description}
\section{Velocity Selector Driver}
This is a driver for velocity selectors as used at SANS machines. A
velocity selector is a kind of turbine which selects wavelength
through rotation speed. Though it rotates fast it is not a chopper,
which are handled in SICS through the general controller driver
described below. The velocity selector driver data structure includes:
\begin{verbatim}
typedef struct __VelSelDriv {
void *pPrivate;
void (*DeletePrivate)(void *pData);
float fTolerance;
int (*Halt)(pVelSelDriv self);
int (*GetError)(pVelSelDriv self,
int *iCode, char *pError,
int iErrlen);
int (*TryAndFixIt)(pVelSelDriv self,
int iCode);
int (*GetRotation)(pVelSelDriv self,
float *fRot);
int (*SetRotation)(pVelSelDriv self,
float fRot);
int (*GetStatus)(pVelSelDriv self,
int *iCall, float *fCur);
int (*GetDriverText)(pVelSelDriv self,
char *pText,
int iTextLen);
int (*GetLossCurrent)(pVelSelDriv self,
float *fLoss);
int (*Init)(pVelSelDriv self,
SConnection *pCon);
}VelSelDriv;
\end{verbatim}
All functions take a pointer to their driver data structure as an
argument. If not stated otherwise they return 1 on success, 0 on failure.
The fields:
\begin{description}
\item[pPrivate] a pointer to a driver private data structure.
\item[DeletePrivate] a function which releases any memory associated
with pPrivate. DeletePrivate is called with a pointer to the driver
private data pPrivate as argument.
\item[fTolerance] This driver assumes it has reached the target speed
if the speed difference target speed - read speed is less then this
tolerance value for four consecutive times.
\item[Halt] stops the velocity selector.
\item[GetError] returns an error code in *iCode and iErrlen
bytes of textual description of the last error on the velocity
selector in pError.
\item[TryAndFixIt] tries to fix the error defined through iCode. iCode
should be the value as returned in *iCode in GetError. This function
returns:
\begin{description}
\item[VELOREDO] redo last operation, error fixed.
\item[VELOFAIL] cannot fix the error from software.
\end{description}
\item[GetRotation] reads the rotation speed into *fRot.
\item[SetRotation] sets a new rotation speed fRot for the selector. Do
NOT wait until finished.
\item[GetStatus] is used to poll for the status of the last driving
operation. It returns:
\begin{description}
\item[VSACCEL] when the velocity selector is accelerating.
\item[VSFAIL] if the status cannot be read.
\item[VSOK] when the velocity seelctor has reached its target speed.
\end{description}
The current rotation speed is returned in *fCur. iCall is a value
which indicates in which state the selector is:
\begin{description}
\item[ROTMOVE] normal running.
\item[ROTSTART] starting the velocity selector. The Dornier velocity
selector have a certain minimum speed. If they are standing they have
to be started first.
\end{description}
\item[GetDriverText] returns iTextLen bytes of additional status
information in pText. This is a list name: value pairs separated by
commata. This is meant to hold additional selector readouts such as
vacuum states, temperatures etc.
\item[GetLossCurrent] initiates a measurement of the loss current of
the velocity selector. The result is returned in *fLoss.
\item[Init] initiates a connection to a velocity selector.
\end{description}
It may be possible that this driver is not very general. It was
developed for Dornier velocity selectors because these were the only
one seen.
\section{General Controller Driver}
This is driver for a SICS general controller object. SICS sports a
general controller object which allows to read a selection of parameters and
to set some parameters. Adapters exists which implement the driveable
or environment interface for parameters in such a controller. The
parameters supported are part of the drivers interface. This
scheme is currently used to control choppers in SICS, but it is not
restricted to this usage. The driver:
\begin{verbatim}
typedef struct __CODRI {
int (*Init)(pCodri self);
int (*Close)(pCodri self);
int (*Delete)(pCodri self);
int (*SetPar)(pCodri self,
char *parname,
float fValue);
int (*SetPar2)(pCodri self,
char *parname,
char *value);
int (*GetPar)(pCodri self,
char *parname,
char *pBuffer,
int iBufLen);
int (*CheckPar)(pCodri self,
char *parname);
int (*GetError)(pCodri self, int *iCode,
char *pError,
int iErrLen);
int (*TryFixIt)(pCodri self, int iCode);
int (*Halt)(pCodri self);
char *pParList;
void *pPrivate;
}Codri;
\end{verbatim}
All functions take a pointer to their driver data structure as an
argument. If not stated otherwise they return 1 on success, 0 on failure.
The fields:
\begin{description}
\item[Init] initializes a connection to the controller and the driver.
\item[Close] closes the connection to a controller.
\item[Delete] releases all memory associated with this drivers private
data structure pPrivate.
\item[SetPar] sets the parameter parname to a new value fValue.
\item[SetPar2] same as SetPar but with new value as text.
\item[GetPar] read the current value of parname into pBuffer. The
value is formatted as text. At max iBufLen bytes are copied into
pBuffer.
\item[CheckPar] checks the progress of setting parname. CheckPar
returns:
\begin{description}
\item[HWFault] If there is a fault in the hardware or the status
cannot be read.
\item[HWBusy] The parameter is still driving.
\item[HWIdle] The parameter has finished changing and is idle.
\end{description}
\item[GetError] returns an error code in *iCode and iErrlen
bytes of textual description of the last error on the velocity
selector in pError.
\item[TryAndFixIt] tries to fix the error defined through iCode. iCode
should be the value as returned in *iCode in GetError. This function
returns:
\begin{description}
\item[CHREDO] redo last operation, error fixed.
\item[CHFAIL] cannot fix the error from software.
\end{description}
\item[Halt] stop any driving parameters.
\item[pParList] a comma separated list of parameters supported by this
driver.
\item[pPrivate] a driver private data structure.
\end{description}