- Updated Makefiles
- Moved TAS code to psi - Updated programmers documentation SKIPPED: psi/make_gen psi/nextrics.c psi/t_conv.c psi/t_conv.f psi/t_rlp.c psi/t_rlp.f psi/t_update.c psi/t_update.f psi/hardsup/el734_utility.c psi/hardsup/makefile_alpha
This commit is contained in:
637
doc/programmer/command.tex
Normal file
637
doc/programmer/command.tex
Normal file
@ -0,0 +1,637 @@
|
||||
\chapter{Writing new SICS Commands}
|
||||
If you wish to write a new command or add a completely new piece of hardware to
|
||||
SICS, this is the chapter to study. There are two ways to implement
|
||||
new commands into SICS: hrough the internal Tcl scripting language and
|
||||
in ANSI--C. This section describes command writing in ANSI--C. You
|
||||
should consider using Tcl when:
|
||||
\begin{itemize}
|
||||
\item The new command is very instrument specific. Rather extend a
|
||||
SICS command in order to support your script.
|
||||
\item The new command is a local syntax fudge.
|
||||
\item The hardware device is an auxiliary, such a He level meter
|
||||
etc.
|
||||
\end{itemize}
|
||||
On the other hand there are indications when to write in ANSI--C:
|
||||
\begin{itemize}
|
||||
\item Complex calculations need to be carried out.
|
||||
\item Large amounts of data need to be handled.
|
||||
\item The code requires extensive bit twiddling.
|
||||
\item A new general SICS facility is provided.
|
||||
\end{itemize}
|
||||
|
||||
|
||||
\section{Mapping Object Oriented Concepts into ANSI--C}
|
||||
SICS is in principle an object oriented system. However, it is implemented
|
||||
in ANSI--C. Therefore object oriented concepts must be mapped into C. The
|
||||
three object oriented concepts which need to be mapped are:
|
||||
\begin{itemize}
|
||||
\item Data Encapsulation.
|
||||
\item Polymorphism.
|
||||
\item Inheritance.
|
||||
\end{itemize}
|
||||
|
||||
Of these, data encapsulation is by far the most important concept. Objects
|
||||
in computer science can be understood as little boxes which contain some
|
||||
data describing their state and which understand messages sent by other
|
||||
objects. If such a message comes in, the object performs some action,
|
||||
perhaps changes its internal state or sends new messages to other objects.
|
||||
It is understood that changes to the internal data of the object can be
|
||||
achieved only through messages to the object and not by directly manipulating
|
||||
variables from the outside. In ANSI--C an object maps to a structure holding
|
||||
the objects data and the messages map to functions which act upon the data
|
||||
structure. In order to do this, the functions must take a pointer to the
|
||||
objects data structure as first parameter. In order to prevent messing with
|
||||
an objects data structure, only a pointer to a structure is declared in the
|
||||
header file. The actual definition of the data structure happens only in
|
||||
the implementation file. All functions belonging to an object are defined in that
|
||||
implementation file and have full access to the data structure.
|
||||
Users of the object see only the header file and thus only a pointer to the
|
||||
objects data structure which prevents
|
||||
them from messing with the objects data directly.
|
||||
In order to illustrate the concepts lets look at
|
||||
a primitive integer object defined in such a way.
|
||||
\begin{verbatim}
|
||||
/*-----------------------------------------------------------------------
|
||||
ExampleInt.h
|
||||
------------------------------------------------------------------------*/
|
||||
typedef struct __ExampleInt *pExampleInt;
|
||||
|
||||
int GetInt(pExampleInt self);
|
||||
void SetInt(pExampleInt self, int iNew);
|
||||
/*------------------- EOF ExampleInt.h---------------------------------*/
|
||||
|
||||
/*----------------------------------------------------------------------
|
||||
ExampleInt.c, Implementation file
|
||||
-----------------------------------------------------------------------*/
|
||||
typedef struct __ExampleInt {
|
||||
int iExample;
|
||||
} ExampleInt;
|
||||
/*--------------------------------------------------------------------*/
|
||||
int GetInt(pExampleInt self)
|
||||
{
|
||||
return self->iExample;
|
||||
}
|
||||
/*---------------------------------------------------------------------*/
|
||||
void SetInt(pExampleInt self, int iNew)
|
||||
{
|
||||
self->iExample = iNew;
|
||||
}
|
||||
\end{verbatim}
|
||||
Using this scheme all code changing the internal state of an object lives
|
||||
in one file. Changes to the objects data structure affect only the
|
||||
implementation file and no other files.
|
||||
This scheme is used for almost all SICS objects. A few system objects and
|
||||
older SICS objects define their data structures in header files. This is
|
||||
either a relic or had to be done for performance reasons.
|
||||
|
||||
The next concept is polymorphism. This describes the situation when a group
|
||||
of objects respond to the same message but doing different things. For
|
||||
instance a whole set of objects would implement a write functionality
|
||||
which writes the objects state to a file. Higher level would then not need
|
||||
to know of which type the actual object is, it just can send the write message
|
||||
and the rest is taken care of by the object. This concept is used for all
|
||||
hardware drivers in SICS. Mapping this to C requires to expose the objects
|
||||
data structure and let the data structure include a pointer to that polymorphic
|
||||
function. As an example, the ExampleInteger with a write function:
|
||||
\begin{verbatim}
|
||||
/*-----------------------------------------------------------------------
|
||||
ExampleInt.h
|
||||
------------------------------------------------------------------------*/
|
||||
typedef struct __ExampleInt{
|
||||
int iExample;
|
||||
void (*write)(struct __ExampleInt *self, FILE *fd);
|
||||
} *pExampleInt, ExampleInt;
|
||||
|
||||
pExampleInt MakeInt(int iNew);
|
||||
|
||||
int GetInt(pExampleInt self);
|
||||
void SetInt(pExampleInt self, int iNew);
|
||||
/*------------------- EOF ExampleInt.h---------------------------------*/
|
||||
|
||||
/*----------------------------------------------------------------------
|
||||
ExampleInt.c, Implementation file
|
||||
-----------------------------------------------------------------------*/
|
||||
static void ExampleWrite(struct _-ExampleInt *self, FILE *fd)
|
||||
{
|
||||
fprintf(fd,"INT = %d",self->iExample);
|
||||
}
|
||||
/*---------------------------------------------------------------------*/
|
||||
pExampleInt MakeInt(int iNew)
|
||||
{
|
||||
pExampleInt pNew = NULL;
|
||||
|
||||
pNew = (pExampleInt)malloc(sizeof(ExampleInt));
|
||||
pNew->iExample = iNew;
|
||||
pNew->write = ExampleWrite;
|
||||
return pNew;
|
||||
}
|
||||
.
|
||||
.
|
||||
.
|
||||
\end{verbatim}
|
||||
This can then be called:
|
||||
\begin{verbatim}
|
||||
void SomeFunc()
|
||||
{
|
||||
pExampleInt pTest;
|
||||
|
||||
pTest = MakeInt(25);
|
||||
.
|
||||
.
|
||||
.
|
||||
pTest->write(pTest,fd);
|
||||
}
|
||||
\end{verbatim}
|
||||
This example also illustrates the concept of a special function which creates
|
||||
a new object of the appropriate type and initializes its data structure
|
||||
properly.
|
||||
|
||||
The last concept to discuss is inheritance. Inheritance can be used when
|
||||
an object is a derivative of another object. For instance a truck is a
|
||||
derivative of a motor car. Much of the behavior of a motor car will be the
|
||||
same as for a truck. In order to prevent rewriting of code, the truck
|
||||
should use the same data structures and code as the motor car. And add
|
||||
or modify only what is special. Inheritance is not much used in SICS. It can
|
||||
be implemented by overlaying data structures. This means the derived
|
||||
classes data structure has the same fields in the same order as the parent
|
||||
class and adds its specials at the end. For example:
|
||||
\begin{verbatim}
|
||||
typedef struct __MotorCar {
|
||||
int iWheels;
|
||||
float fSpeed;
|
||||
} *pMotorCar, MotorCar;
|
||||
|
||||
|
||||
typedef struct __Truck {
|
||||
int iWheels;
|
||||
float fSpeed; /* same as MotorCar */
|
||||
double dPayLoad; /* special for Truck */
|
||||
} *pTruck, Truck;
|
||||
|
||||
\end{verbatim}
|
||||
Thus functions defined for motor car can operate on trucks as well.
|
||||
For more details study the relationship between the ITC4 controller and
|
||||
general environment controllers. This is the only place where SICS
|
||||
currently uses inheritance.
|
||||
|
||||
|
||||
\section{Command Writing Basics}
|
||||
\subsubsection{The Object Wrapper Function}
|
||||
The first thing needed in order to implement a new command in SICS is
|
||||
the object wrapper function. This function has the following
|
||||
signature:
|
||||
\begin{verbatim}
|
||||
int ObjectWrapper(SConnection *pCon, SicsInterp *pSics, void *pData,
|
||||
int argc, char *argv[]);
|
||||
\end{verbatim}
|
||||
The parameters are:
|
||||
\begin{description}
|
||||
\item[pCon] A pointer to the connection object representing the client
|
||||
invoking this command.
|
||||
\item[pSics] A pointer to the SICS interpreter.
|
||||
\item[pData] A pointer to a user data structure for this object. Can
|
||||
be NULL if no such thing exists.
|
||||
\item[argc] Number of arguments in argv to this function.
|
||||
\item[argv] Arguments to this function. The argc, argv scheme is
|
||||
the same as for a C--language main function. The first argument is
|
||||
always the name of the object.
|
||||
\end{description}
|
||||
This object wrapper function must evaluate its arguments, do what it
|
||||
is meant to do and write any results to the client connection. In the
|
||||
case of an error this function must return 0, in case of success 1.
|
||||
|
||||
|
||||
\subsubsection{The Object Data Structure}
|
||||
Only the most primitive objects or commands get away without an own
|
||||
data structure. Usually a data structure will be needed by SICS
|
||||
objects in order to keep configuration parameters etc. A pointer to
|
||||
such a datastructure is passed into the object wrapper function as the
|
||||
pointer pData. This object data structure has to fulfill some
|
||||
conditions in order to live happily within SICS. And actually, not
|
||||
only the datastructure is needed but also a function which is able to
|
||||
release any memory allocated by the datastructure. SICS needs this
|
||||
function in order to clean things up properly.
|
||||
|
||||
|
||||
A valid SICS object structure has to look like this:
|
||||
\begin{verbatim}
|
||||
typedef struct __MyObject {
|
||||
pObjectDescriptor pDes;
|
||||
int iMyExtremlyImportantInt;
|
||||
.
|
||||
.
|
||||
.
|
||||
} MyObject;
|
||||
\end{verbatim}
|
||||
Please note that the first item in the data structure MUST be a
|
||||
pointer to an SICS object descriptor. Add your own stuff below
|
||||
that. If you do not adhere to this requirement, SICS will dump core on
|
||||
you rather sooner then later.
|
||||
|
||||
|
||||
SICS needs this object descriptor for its own internal purposes. The
|
||||
object descriptor is again a data structure with this signature:
|
||||
\begin{verbatim}
|
||||
typedef struct {
|
||||
char *name;
|
||||
int (*SaveStatus)(void *self, char *name,FILE *fd);
|
||||
void *(*GetInterface)(void *self, int iInterfaceID);
|
||||
} ObjectDescriptor, *pObjectDescriptor;
|
||||
|
||||
\end{verbatim}
|
||||
\begin{description}
|
||||
\item[name] This is a type identifier for the object. SICS uses this
|
||||
identifier for run time type identification (RTTI). For example this
|
||||
field says Motor for motors, Counter for counters etc.
|
||||
\item[SaveStatus] is the function called by the SICS status backup
|
||||
mechanism. The default implementation of this function does
|
||||
nothing. But if your new object has to store configuration commands
|
||||
into the status file you should create an own function with the same
|
||||
signature and assign this function to the object descriptors
|
||||
SaveStatus. A suitable function will print the necessary commands to
|
||||
achieve the same configuration as the current state into the file
|
||||
represented by fd.
|
||||
\item[GetInterface] SICS objects can have various capabilities: a
|
||||
motor can be driven or scanned, you may count on a counter etc. Such
|
||||
capabilities are expressed as interfaces(see \ref{interface}) in SICS.
|
||||
There is an integer ID for each of those interfaces. GetInterface now
|
||||
returns a suitable
|
||||
interface in return for a given interface ID or NULL if the object
|
||||
does not implement the interface. The default version of this function
|
||||
retuns NULL always. If your object implements an interface, this
|
||||
function has to be overloaded to return this interface on demand.
|
||||
\end{description}
|
||||
|
||||
A default object descriptor can be created with:
|
||||
\begin{verbatim}
|
||||
pObjectDescriptor CreateDescriptor(type);
|
||||
\end{verbatim}
|
||||
with type being the object type identifier. A descriptor can be
|
||||
deleted with:
|
||||
\begin{verbatim}
|
||||
DeleteDescriptor(pDes);
|
||||
\end{verbatim}
|
||||
|
||||
|
||||
As already mentioned, a function to release any memory allocated for
|
||||
the object data structure is also needed. Its signature is simple:
|
||||
\begin{verbatim}
|
||||
void KillObject(void *pdata);
|
||||
\end{verbatim}
|
||||
with pdata being the object to delete.
|
||||
|
||||
|
||||
\subsection{Installing the new Command into the SICS Interpreter}
|
||||
A new command can be installed into SICS with the function:
|
||||
\begin{verbatim}
|
||||
int AddCommand(SicsInterp *pSics,
|
||||
char *name,
|
||||
ObjectFunc ofunc,
|
||||
KillFunc killo,
|
||||
void *objectData);
|
||||
\end{verbatim}
|
||||
with pSics being the interpreter into which to install the command,
|
||||
name the name of the command, ofunc its wrapper function, killo the
|
||||
object data structure deletion function and objectData being a pointer
|
||||
to the object data structure. If no data structure is defined for the
|
||||
command, killo and objectData must be NULL.
|
||||
|
||||
|
||||
Now the question arise where AddCommand has to be placed. Various
|
||||
cases have to be distinguished: The first case is that the new command
|
||||
is relevant to SICS users in all corners of the world. Then the new
|
||||
command should be added to the function InitIniCommand in file
|
||||
ofac.c.
|
||||
|
||||
The second case is that the command is specific to a certain
|
||||
instrument or a special hardware or implements a local fashion of
|
||||
dealing with things. Then the new command should be installed from the
|
||||
function AddSiteCommand in the site data structure (see \ref{site}).
|
||||
|
||||
Another scheme becomes valid when multiple instances of the object
|
||||
need to be created. \label{factory} For instance the object represents
|
||||
a motor of which you may have many. Then it is useful to create a factory
|
||||
command. This is a special SICS command which creates the desired
|
||||
objects and installs them in the interpreter. This factory command
|
||||
then has to be installed according to the logic given above. Usually
|
||||
such objects are installed in the SICS initialization file. After
|
||||
processing this initialization file the factory command is not useful
|
||||
any longer. Then such factory commands should be removed from the
|
||||
interpreter with:
|
||||
\begin{verbatim}
|
||||
RemoveCommand(SicsInterp *pSics, char *name);
|
||||
\end{verbatim}
|
||||
This has to be placed into the sites member function RemoveSiteCommand
|
||||
or into KillIniCommand in ofac.c, depending where you installed the
|
||||
command in the first place.
|
||||
|
||||
Be careful with commands for deleting objects though. SICS objects may
|
||||
be aliased, used by other objects or connected through the callback
|
||||
interface (see \ref{inter}). SICS does not implement proper
|
||||
bookeeping on all these relationships and thus deleting a command from
|
||||
SICS without taking this into account may cause SICS to dump core on
|
||||
you.
|
||||
|
||||
|
||||
\section{Interacting with the Client Connection}
|
||||
A SICS command writer needs to interact with the client connection for
|
||||
a variety of reasons:
|
||||
\begin{itemize}
|
||||
\item To write error messages and other output
|
||||
\item To prompt for more data
|
||||
\item To check if the user has appropriate privileges for the command
|
||||
implemented.
|
||||
\item To tell upper level code that something went very wrong.
|
||||
\end{itemize}
|
||||
As these tasks are so common you will find that the connection object
|
||||
has to be passed frequently into lower level code as an argument.
|
||||
|
||||
\subsection{Writing and Reading to the Client}
|
||||
All I/O to the client has to be processed through the I/O functions
|
||||
for connections defined in conman.h and implemented in conman.c. The
|
||||
most bable of these are SCWrite and SCPrompt.
|
||||
These function will now be inspected in more detail:
|
||||
\begin{verbatim}
|
||||
int SCWrite(SConnection *pCon, char *pText, int eCode);
|
||||
int SCPrompt(SConnection *pCon, char *pPrompt, char *pBuffer, int iBufLen);
|
||||
\end{verbatim}
|
||||
SCWrite writes the data pText to the connection specified by pCon. The
|
||||
parameter eCode denotes the output code of the data in pText. SICS clients
|
||||
can choose to suppress some I/O from the SICS server. For instance a
|
||||
GUI--client might chooses not to receive scan status reports. For this
|
||||
purpose it was necessary to stick an output code onto each message to the
|
||||
client. Possible output codes are: eError, eWarning, eValue, eStatus and
|
||||
some internal codes. The names are self explaining. eValue denotes a value
|
||||
which has been explicitly asked for by the client. The rule specified above
|
||||
becomes understandable and thus bearable when looking at all the things
|
||||
SCWrite does with the message:
|
||||
\begin{itemize}
|
||||
\item It is written to the client connection socket, subject to the output
|
||||
code specified.
|
||||
\item The message is written to all log files configured for the client
|
||||
connection.
|
||||
\item If the client privilege is useer or manager, the data isw ritten
|
||||
to the command log.
|
||||
\item The message is written to the server log together with the socket
|
||||
number of the connection.
|
||||
\item SCWrite stores the message into the Tcl macro interpreter in order to
|
||||
enable processing of data from SICS commands in Tcl scripts.
|
||||
\item SCWrite suppresses all messages to the client while executing a macro.
|
||||
This stops spurious output to appear at the client connection when running a
|
||||
command defined in the macro language. The exception are messages of type
|
||||
eError and eWarning. Such messages are always sent to the client.
|
||||
\end{itemize}
|
||||
|
||||
SCPrompt prompts the user at the client connection for data. The prompt
|
||||
string pPrompt is sent. Data entered by the user is returned in buffer
|
||||
pBuffer. Maximum iBufLen character are returned. While waiting for client to
|
||||
provide data, the SICS task switcher runs.
|
||||
|
||||
There is another convenience function SCSendOK(SConnection *pCon) which is
|
||||
just a wrapper around SCWrite. SCSendOk sends an 'OK' to the client. It is good
|
||||
practice to let the user know that the operation requested had been
|
||||
performed.
|
||||
|
||||
There are some more conventions which are useful to adhere to:
|
||||
\begin{itemize}
|
||||
\item All error messages start with the string ERROR:
|
||||
\item All warnings start with the string WARNING:
|
||||
\item All requested values are returned in the format name = value.
|
||||
\end{itemize}
|
||||
|
||||
There exist special functions to send mass data through a connection
|
||||
in either uuencoded or zipped form. The latter works only through
|
||||
plain socket connections, telnet will mess up binary data. Please
|
||||
note, that numeric data sent by the SICS server needs to be in network
|
||||
byte order in order to be understood by the Java clients. Further
|
||||
functions allow to tailor writing behavious further by overloading the
|
||||
operations performed by SCWrite. This is documented in conman.h
|
||||
|
||||
|
||||
\subsection{Checking Client Privileges}
|
||||
One task any SICS object has to perform is to check if a client,
|
||||
represented through a connection object, is privileged to perform a
|
||||
requested operation. The most useful function to do this is:
|
||||
\begin{verbatim}
|
||||
int SCMatchRights(SConnection *pCon, int rights);
|
||||
\end{verbatim}
|
||||
This function not only checks if the user has at least the user rights
|
||||
given as parameter rights but also checks if the SICS server is
|
||||
currently performing a scan or something. It is generally a bad idea
|
||||
to change parameters while the instrument is taking a measurement. If
|
||||
all is good 1 is returned, 0 in case of trouble. All troubles will
|
||||
already have been reported to the client by this function. SICS knows
|
||||
four levels of user rights:
|
||||
\begin{description}
|
||||
\item[usSpy] may look at things but change nothing.
|
||||
\item[usUser] may do those things a normal user is supposed to do.
|
||||
\item[usMugger] may perform configuration tasks.
|
||||
\item[usInternal] absolutely no restrictions, used only internally.
|
||||
\end{description}
|
||||
|
||||
|
||||
There are further functions for requesting client and setting client
|
||||
rights codes. These functions are all defined in conman.h.
|
||||
|
||||
\subsection{Interrupting}
|
||||
On occasion a SICS object may come to the conclusion that an error is
|
||||
so bad that the measurement needs to be stopped. Clearly a means is
|
||||
needed to communicate this to upper level code. This means is setting
|
||||
an interrupt on the connection.
|
||||
The current interrupt active interrupt is located at the connection object
|
||||
and can be retrieved with {\bf SCGetInterrupt} and set with {\bf
|
||||
SCSetInterrupt}. Interrupt codes are defined in interrupt.h and are ordered
|
||||
into a hierarchy:
|
||||
\begin{description}
|
||||
\item[eContinue] Everything is just fine.
|
||||
\item[eAbortOperation] Stop the current scan point or whatever is done,
|
||||
but do not stop altogether.
|
||||
\item[eAbortScan] Abort the current scan, but continue processing of further
|
||||
commands in R\"unbuffers or command files.
|
||||
\item[eAbortBatch] Aborts everything, operations, scans and batch processing
|
||||
and leaves the system ready to enter new commands.
|
||||
\item[eHaltSystem] As eAbortBatch, but lock the system.
|
||||
\item[eFreeSystem] Unlocks a system halted with eHaltSystem.
|
||||
\item[eEndServer] Makes the SICS server run down and exit.
|
||||
For internal usage only.
|
||||
\end{description}
|
||||
|
||||
Higher level SICS objects may come to the conclusion that the error
|
||||
reported by lower level code is actually not that critical and clear
|
||||
any pending interrupts by setting the interrupt code to eContinue and
|
||||
thus consume the interrupt.
|
||||
|
||||
\subsection{Communicating with all Clients}
|
||||
Two facilities exist which allow one SICS command to reach out to all
|
||||
connected clients.
|
||||
\begin{itemize}
|
||||
\item There is a function which writes a message to all clients. This
|
||||
is ServerWriteGlobal(char *text, int outCode);
|
||||
\item There exists a global status code which can be set with
|
||||
SetStatus and retrieved with GetStatus. See status.h for more
|
||||
details. Use this when your SICS command starts lengthy operations
|
||||
such as driving or counting.
|
||||
\end{itemize}
|
||||
|
||||
\section{Using other SICS Objects}
|
||||
In most cases a new command needs to make use of other SICS
|
||||
objects. Before another SICS object can be used, it must be found
|
||||
within the SICS interpreter. In order do this the name of the object
|
||||
is obviously needed. This must be a configuration parameter or passed
|
||||
in as a argument to the command. In general it is also necessary to
|
||||
check if this name points to the right type of object. All this can be
|
||||
achieved with the function:
|
||||
\begin{verbatim}
|
||||
void *FindCommandData(SicsInterp *pSics, char *name, char *type);
|
||||
\end{verbatim}
|
||||
This function tries to find a command name in the interpreter and also
|
||||
checks if the objects type descriptor (the name parameter in the
|
||||
object descriptor structure) matches type. If this is so, a pointer to
|
||||
the objects data structure is returned. If one of the test fails, NULL
|
||||
is returned. Suitable parameters for type can be found by searching
|
||||
for CreateDescriptor in the implementation file of the desired
|
||||
object. After a cast to the proper pointer type, all the functions
|
||||
defined for the object and documented in its header file can be used.
|
||||
|
||||
\subsection{Running Motors and Counters}
|
||||
There are special rules which apply if a new command is written which
|
||||
coordinates motors and counters. For instance a special scan or drive
|
||||
command. It is important that such higher level code starts motors,
|
||||
virtual motors and counters through the interfaces defined by the
|
||||
device executor. The device executor guarantees proper monitoring of
|
||||
the device. The relevant functions are:
|
||||
\begin{verbatim}
|
||||
int StartMotor(pExeList self, SicsInterp *pSics, SConnection *pCon,
|
||||
char *name, float fNew);
|
||||
int StartCounter(pExeList self, SicsInterp *pSics, SConnection
|
||||
*pCon, char *name);
|
||||
|
||||
\end{verbatim}
|
||||
StartMotor starts the motor name to run to the new value
|
||||
fNew. StartCounter starts the counter name. The counter must have been
|
||||
loaded with proper presets etc. with the appropriate function
|
||||
calls. The device executor hides behind the pExeList pointer. This is
|
||||
always accessible through the global pointer: pServ->pExecutor.
|
||||
|
||||
Once a counter or motor has been started, quite often the command can
|
||||
only continue if the operation has finished. But during this time the
|
||||
SICS server should be responsive to other clients. In order to do this
|
||||
we have to wait for the device executor task to finish. A code
|
||||
fragment implementing all this for a count operation is shown below:
|
||||
\begin{verbatim}
|
||||
/*-------------- count */
|
||||
iRet = StartCounter(pServ->pExecutor, pSics,
|
||||
pCon,
|
||||
``ScanCounter'');
|
||||
if(!iRet)
|
||||
{
|
||||
SCWrite(self->pCon,"ERROR: Cannot Count, Scan aborted",eError);
|
||||
return 0;
|
||||
}
|
||||
/* get the ID of the device executor task */
|
||||
lTask = GetDevexecID(pServ->pExecutor); /* get ID of device
|
||||
executor task */
|
||||
if(lTask > 0);
|
||||
{
|
||||
/* wait for the device executor to finish */
|
||||
TaskWait(pServ->pTasker,lTask);
|
||||
}
|
||||
|
||||
/* finished, check for interrupts. Whatever happened, user
|
||||
interrupt or HW interrupt, it will be on our connection
|
||||
*/
|
||||
iInt = SCGetInterrupt(self->pCon);
|
||||
switch(iInt)
|
||||
{
|
||||
case eContinue:
|
||||
break;
|
||||
case eAbortOperation:
|
||||
continue;
|
||||
break;
|
||||
case eAbortScan:
|
||||
SCWrite(self->pCon,"ERROR: Scan aborted",eError);
|
||||
/* eat the interrupt, the requested op has been
|
||||
done
|
||||
*/
|
||||
SCSetInterrupt(self->pCon,eContinue);
|
||||
return 0;
|
||||
break;
|
||||
default: /* all others */
|
||||
SCWrite(self->pCon,"ERROR: Scan aborted",eError);
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
\end{verbatim}
|
||||
This code also shows the necessary error checking. It also shows how
|
||||
to check for possible interrupts after such an operation. It is very
|
||||
advisable to do this because the user may have interrupted the
|
||||
process. And she might not be all to happy if the new command just
|
||||
continues with the next step rather then aborting the process.
|
||||
|
||||
|
||||
\section{SICS Interfaces}\label{interface}
|
||||
The point about SICS interfaces can best be deduced from an example:
|
||||
Everybody expects that a motor can be operated through a drive command
|
||||
or scanned in a scan. But there are other things which should be
|
||||
operated through a drive or scan commands too: environment controllers
|
||||
(temperature), virtual motors, chopper speeds, chopper phases and
|
||||
possibly other things. In order to make upper level scan or drive
|
||||
commands independent of the implementation of the actual operation it
|
||||
is useful to have an abstract interface for everything which can be
|
||||
driven or scanned. This is the drivable interface. Any such object
|
||||
which should be driven or scanned has to implement this interface.
|
||||
|
||||
Several of these interfaces exist in SICS:
|
||||
\begin{description}
|
||||
\item[Drivable] The drivable interface for everything which can be
|
||||
moved and takes some time to complete its movement.
|
||||
\item[Countable] Everything which counts: counters, histogram memories
|
||||
etc.
|
||||
\item[Environment] An interface which allows for monitoring of a
|
||||
parameter through the environment monitor. Usually these are sample
|
||||
environment things but it could also be chopper phases etc.
|
||||
\item[Callback] This is an interface which allows object A to call a
|
||||
special function, the callback function, in the context of object B
|
||||
whenever a certain event in A occurs. This is a way to automatically
|
||||
link object together in a component programming manner. This is also
|
||||
used for automatic notification of status clients when instrument
|
||||
parameters change.
|
||||
\end{description}
|
||||
|
||||
There are several situations when the SICS interfaces have to be
|
||||
considered:
|
||||
\begin{itemize}
|
||||
\item When hacking SICS kernel code or replacing parts of it.
|
||||
\item The driveable interface should be implemented by virtual
|
||||
motors. Virtual motors are objects which realize complex movements
|
||||
possibly involving multiple motors. Examples include driving
|
||||
wavelength (theta,two theta and possbly curvature motors have to be
|
||||
moved) or omega two theta.
|
||||
\item Any time objects are introduced into SICS which repesent
|
||||
completely new hardware.
|
||||
\item When automatical notifications between objects are needed, use
|
||||
the callback interface.
|
||||
\end{itemize}
|
||||
|
||||
Adding any such interface to your new SICS object involves the
|
||||
following steps:
|
||||
\begin{itemize}
|
||||
\item Add a data structure representing the interface to your objects
|
||||
data structure.
|
||||
\item Write all the functions required by the interface.
|
||||
\item Populate the interface data structure with the pointers to your
|
||||
function implementations.
|
||||
\item Write a new GetInterface function for the object descriptor
|
||||
which returns your new interface when requested and assign it to your
|
||||
object descriptors GetInterface field. SICS needs this in order to be
|
||||
able to find the objects new interface.
|
||||
\end{itemize}
|
||||
|
||||
The interfaces available are documented in the files interface.w,
|
||||
interface.h and interface.tex in the main SICS directory and through
|
||||
numerous examples in the source code.
|
||||
|
||||
A not overly complex example for the implementation of an interface is
|
||||
the code in o2t.* which implements the coupled driving of two motors
|
||||
where the second is always the double of the value of the first. This
|
||||
is for omega two-theta scans.
|
@ -3,7 +3,7 @@ In this chapter the facilities of the SICS servers kernel will be examined
|
||||
more closely. All the kernel modules and their function will be listed,
|
||||
together with some explanatory information and an overview about the
|
||||
application programmers interfaces (API) provided. This section should
|
||||
answer the questions: WHat is available?, Where to find what?,
|
||||
answer the questions: What is available?, Where to find what?,
|
||||
Why did they do that? Details of
|
||||
the API's mentioned are given in the reference section.
|
||||
|
||||
@ -124,7 +124,7 @@ The network reader currently supports four types of sockets:
|
||||
\item User sockets.
|
||||
\end{itemize}
|
||||
|
||||
The accept type of socket is the main server port where clients try to
|
||||
The accept type of socket is the main server port to which clients try to
|
||||
connect to. The network reader accepts the connection and tries to read a
|
||||
username/password pair for a specified amount of time.
|
||||
If the username/password is valid, the connection will be accepted,
|
||||
@ -175,16 +175,18 @@ mechanism. For more details see John Ousterhout's book.
|
||||
In an earlier stage it was considered to use the Tcl interpreter as the SICS
|
||||
interpreter. This idea was discarded for some reasons: One was the
|
||||
difficulty of transporting the client execution context (i.e. the connection
|
||||
object) through the Tcl interpreter. There is no standard Tcl mechanism for
|
||||
doing that. The second was security: the Tcl
|
||||
interpreter is very powerful and can be abused. It was felt that the system
|
||||
had to be protected against such problems. The third reasons was that the
|
||||
set of user commands should not be cluttered with Tcl commands in order to
|
||||
prevent confusion. Programming macros is anyway something which is done by
|
||||
SICS managers or programmers. However, the SICS interpreter is still modeled
|
||||
very much like the Tcl-interpreter. A Tcl interpreter is still included in
|
||||
order to provide a full featured macro language. The SICS interpreter and the
|
||||
Tcl macro interpreter are still tightly coupled.
|
||||
object) through the Tcl interpreter. This reason has become invalid
|
||||
now, with the advent of Tcl 8.+ which supports namespaces. The second
|
||||
was security: the Tcl interpreter is very powerful and can be
|
||||
abused. It was felt that the system had to be protected against such
|
||||
problems. The third reasons was that the set of user commands should
|
||||
not be cluttered with Tcl commands in order to prevent
|
||||
confusion. Programming macros is anyway something which is done by
|
||||
SICS managers or programmers. However, the SICS interpreter is still
|
||||
modeled very much like the Tcl-interpreter. A Tcl interpreter is
|
||||
still included in order to provide a full featured macro
|
||||
language. The SICS interpreter and the Tcl macro interpreter are
|
||||
still tightly coupled.
|
||||
|
||||
The SICS interpreter must forward commands to the SICS objects. For this the
|
||||
interpreter needs some help from the objects themselves. Each SICS object
|
||||
@ -290,26 +292,6 @@ important SICS components: the interpreter, the task switcher, the device
|
||||
executor, the environment monitor and the network reader. This module also
|
||||
contains the code for initializing, running and stopping the server.
|
||||
|
||||
\section{The ServerLog}
|
||||
As part of the SICS kernel there exists a global server log file. This file
|
||||
contains:
|
||||
\begin{itemize}
|
||||
\item All traffic on all client connections. Even messages suppressed by the
|
||||
clients.
|
||||
\item All internal error messages.
|
||||
\item Notifications about important internal status changes.
|
||||
\end{itemize}
|
||||
This server log is meant as an aid in debugging the server. As the SICS
|
||||
server may run for days, weeks and months uninterrupted this log file may
|
||||
become very large. However, only the last thousand or so messages are really
|
||||
of interest when tracking a problem. Therefore a scheme is implemented to
|
||||
limit the disk space used by the server log. The server log writes
|
||||
cyclically into a number of files. A count of the lines is kept which were
|
||||
written to each file. Above a predefined count, a new file is started.
|
||||
As an interface the server log provides a function which allows to write
|
||||
a message to it. This can be used by any object in the system for
|
||||
interesting messages. The number of files to cycle through and the length of
|
||||
each file can be configured by defines at the top of servlog.c.
|
||||
|
||||
\section{The Performance Monitor}
|
||||
This facility provides the data for the Performance (see user documentation)
|
||||
@ -351,5 +333,57 @@ users. If this becomes a serious concern, this module has to be rewritten.
|
||||
\section{The Server Main Function}
|
||||
This does not do much, just initialize the server, run it, and stop it.
|
||||
|
||||
|
||||
|
||||
\section{Logging}
|
||||
The SICS server offers multiple options for logging:
|
||||
\begin{itemize}
|
||||
\item There is a cyclical server log logging all traffic. This is
|
||||
described below.
|
||||
\item Per client connection log files can be configured. This is part
|
||||
of the connection object interface.
|
||||
\item A special module, the commandlog exists, which saves all traffic
|
||||
issued on client connections with user or manager privilege. This is
|
||||
the most useful log for finding problems. This facility can be
|
||||
configured to create a log file per day. Or the user can demand to
|
||||
have her very own log file.
|
||||
\end{itemize}
|
||||
|
||||
|
||||
\subsection{The ServerLog}
|
||||
As part of the SICS kernel there exists a global server log file. This file
|
||||
contains:
|
||||
\begin{itemize}
|
||||
\item All traffic on all client connections. Even messages suppressed by the
|
||||
clients.
|
||||
\item All internal error messages.
|
||||
\item Notifications about important internal status changes.
|
||||
\end{itemize}
|
||||
This server log is meant as an aid in debugging the server. As the SICS
|
||||
server may run for days, weeks and months uninterrupted this log file may
|
||||
become very large. However, only the last thousand or so messages are really
|
||||
of interest when tracking a problem. Therefore a scheme is implemented to
|
||||
limit the disk space used by the server log. The server log writes
|
||||
cyclically into a number of files. A count of the lines is kept which were
|
||||
written to each file. Above a predefined count, a new file is started.
|
||||
As an interface the server log provides a function which allows to write
|
||||
a message to it. This can be used by any object in the system for
|
||||
interesting messages. The number of files to cycle through and the length of
|
||||
each file can be configured by defines at the top of servlog.c.
|
||||
|
||||
|
||||
\section{Instrument Status Persistence}
|
||||
Real programs do dump core (the SICS server is good, but is no
|
||||
exception in this respect) and real computers fall over. In such cases
|
||||
it would be useful if instrument configuration parameters such as
|
||||
zero points , variable settings etc. are not lost. SICS achieves this
|
||||
by writing a status file each time a parameter changes. This
|
||||
status file is read back whenever the SICS server starts. The default
|
||||
status file is configured in the instrument startup file as the SicsOption
|
||||
statusfile. The user
|
||||
can also request a status file to be written or recovered manually.
|
||||
The status file is just a file with SICS commands which configure
|
||||
relevant parameters. The actual writing of these commands is delegated
|
||||
to each SICS object. Each SICS object which whishes to save data into
|
||||
the status file has to implement a function which will
|
||||
automatically be called when a status file is written. For details,
|
||||
consult the chapter on SICS object implementation.
|
||||
|
||||
|
@ -428,7 +428,8 @@ Sometimes error conditions arise in lower level code which should cause all
|
||||
upper level code to finish execution. Such conditions may be the result of a
|
||||
critical hardware fault or may even be requested by a user who wants to
|
||||
abort an operation. A standard method for communicating such conditions
|
||||
through the system is necessary. SICS uses interrupts for such conditions.
|
||||
through the system is necessary.
|
||||
SICS uses interrupts for such conditions.
|
||||
The current interrupt active interrupt is located at the connection object
|
||||
and can be retrieved with {\bf SCGetInterrupt} and set with {\bf
|
||||
SCSetInterrupt}. Interrupt codes are defined in interrupt.h and are ordered
|
||||
|
@ -28,14 +28,20 @@ matches the above criteria.
|
||||
\section{The SINQ Hardware Setup}
|
||||
SICS had to take in account the SINQ hardware setup which had been decided
|
||||
upon earlier on. Most hardware such as motors and counters is controlled via
|
||||
RS--232 interfaces. These devices connect to a Macintosh PC which has a
|
||||
terminal server program running on it. This terminal server program collects
|
||||
request to the hardware from a TCP/IP port and forwards them to the serial
|
||||
device. The instrument control program runs on a workstation running
|
||||
DigitalUnix. Communication with the hardware happens via TCP/IP through the
|
||||
terminal server. Some hardware devices, such as the histogram memory, can handle
|
||||
RS--232 interfaces. These RS--232 interfaces are connected to a
|
||||
terminal server which allows to access such devices through the TCP/IP
|
||||
network.
|
||||
|
||||
For historical reasons the instrument control software does not access
|
||||
the terminal server directly but through another software layer, the
|
||||
SerPortServer program. The SerPortServer program is another TCP/IP
|
||||
server which allows multiple network clients to access the same
|
||||
terminal server port through a home grown protocoll. In the long run
|
||||
this additional software layer will be abolished.
|
||||
|
||||
Some hardware devices, such as the histogram memory, can handle
|
||||
TCP/IP themselves. With such devices the instrument control program
|
||||
communicates directly through TCP/IP, without a terminal server. All
|
||||
communicates directly through TCP/IP. All
|
||||
hardware devices take care of their real time needs themselves. Thus the
|
||||
only task of the instrument control program is to orchestrate the hardware
|
||||
devices. SICS is designed with this setup up in mind, but is not restricted
|
||||
@ -90,15 +96,18 @@ client for a powder diffractometer is given in picture \ref{dmc}
|
||||
The SICS server is the core component of the SICS system. The SICS server is
|
||||
responsible for doing all the work in instrument control. Additionally the
|
||||
server has to answer the requests of possibly multiple clients.
|
||||
The SICS server can be subdivided into three subsystems: The kernel, a database
|
||||
of SICS objects and an interpreter. The SICS server kernel takes care of
|
||||
client multitasking and the preservation of the proper I/O and error context
|
||||
for each client command executing.
|
||||
SICS objects are software modules which represent all aspects
|
||||
of an instrument: hardware devices, commands, measurement strategies
|
||||
The SICS server can be subdivided into three subsystems:
|
||||
\begin{description}
|
||||
\item[The kernel] The SICS server kernel
|
||||
takes care of client multitasking and the preservation of the proper
|
||||
I/O and error context for each client command executing.
|
||||
\item[SICS Object Database] SICS objects are software modules which
|
||||
represent all aspects of an instrument: hardware devices, commands, measurement strategies
|
||||
and data storage. This database of objects is initialized at server startup
|
||||
time from an initialization script. The third SICS server component is an
|
||||
interpreter which allows to issue commands to the objects in the objects database.
|
||||
time from an initialization script.
|
||||
\item[The Interpreter] The interpreter allows to issue commands to the
|
||||
objects in the objects database.
|
||||
\end{description}
|
||||
The schematic drawing of the SICS server's structure is given in picture
|
||||
\ref{newsics}.
|
||||
\begin{figure}
|
||||
@ -130,10 +139,12 @@ executing one after another. The servers main loop does nothing but
|
||||
executing the tasks in this circular buffer in an endless loop.
|
||||
There are several system tasks and one such
|
||||
task for each living client connection. Thus only one task executes at any
|
||||
given time and data access is efficiently serialized. One of the main system
|
||||
given time and data access is efficiently serialized.
|
||||
|
||||
One of the main system
|
||||
tasks (and the one which will be always there) is the network reader. The
|
||||
network reader has a list of open network connections and checks each of
|
||||
them for pending requests. What happens when a data is pending on an open
|
||||
them for pending requests. What happens when data is pending on an open
|
||||
network port depends on the type of port: If it is the servers main
|
||||
connection port, the network reader will try to accept and verify a new
|
||||
client connection and create the associated data structures. If the port
|
||||
@ -190,9 +201,9 @@ Most experiments do not happen at ambient room conditions but
|
||||
require some special environment for the sample. Mostly this is temperature
|
||||
but it can also be magnetic of electric fields etc. Most of such devices
|
||||
can regulate themselves but the data acquisition program needs to monitor
|
||||
such devices. Within SICS this is done via a special system object, the
|
||||
such devices. Within SICS, this is done via a special system object, the
|
||||
environment monitor. A environment device, for example a temperature
|
||||
controller, registers it's presence with this object. Then an special system
|
||||
controller, registers it's presence with this object. Then a special system
|
||||
task will control this device when it is executing, check for possible out
|
||||
of range errors and initiates the proper error handling if such a problem is
|
||||
encountered.
|
||||
@ -241,15 +252,15 @@ to a system of protocols. There are protocols for:
|
||||
\item For checking the authorisation of the client who wants to execute the
|
||||
command.
|
||||
\end{itemize}
|
||||
|
||||
SICS uses NeXus$^{2}$, the upcoming standard for data exchange for neutron
|
||||
and x\_ray scattering as its raw data format.
|
||||
|
||||
SICS objects have the ability to notify clients and other objects of
|
||||
internal state changes. For example when a motor is driven, the motor object
|
||||
can be configured to tell SICS clients or other SICS objects about his new
|
||||
position.
|
||||
|
||||
SICS uses NeXus$^{2}$, the upcoming standard for data exchange for neutron
|
||||
and x\_ray scattering as its raw data format.
|
||||
|
||||
|
||||
\section{SICS Working Examples}
|
||||
In order to get a better feeling for the internal working of SICS the course
|
||||
of a few different requests through the SICS system is traced in this
|
||||
@ -284,7 +295,7 @@ pending commands.
|
||||
\begin{itemize}
|
||||
\item The network reader finds data pending at one of the client ports.
|
||||
\item The network reader reads the command, splits it into single lines and
|
||||
put those on the top of the client connections command stack. The network
|
||||
put those on top of the client connections command stack. The network
|
||||
reader passes control to the task switcher.
|
||||
\item In due time the client connection task executes, inspects its command
|
||||
stack, pops the command pending and forwards it together with a pointer to
|
||||
@ -415,23 +426,18 @@ new commands.
|
||||
driving whenever the task switcher allows it to execute.
|
||||
\item In due time the device executor task will find that the motor finished
|
||||
driving. The task will then die silently. The clients grab of the hardware driving
|
||||
permission will be released. If errors occurred, however a they will be reported.
|
||||
\item At this stage the drive command wrapper function will awake and
|
||||
continue execution. This means inspecting errors and reporting to the client
|
||||
how things worked out.
|
||||
\item This done, control passes back through the interpreter and the connection
|
||||
task to the task switcher. The client connection is free to execute
|
||||
other commands.
|
||||
\item The next task executes.
|
||||
permission will be released. Any errors however, will be reported.
|
||||
\end{itemize}
|
||||
|
||||
All this seems to be pretty complex and time consuming. But it is the complexity needed to
|
||||
do so many things, especially the non blocking mode of operation requested
|
||||
by users. Tests have shown that the task switcher manages +900 cycles per second
|
||||
through
|
||||
the task list on a DigitalUnix machine and 50 cycles per second on a pentium 133mhz
|
||||
machine running linux. Both data were obtained with software simulation of
|
||||
hardware devices. With real SINQ hardware these numbers drop 4 cycles per
|
||||
second. This shows clearly that the communication with the hardware is the
|
||||
systems bottleneck and not the task switching scheme.
|
||||
by users. Tests have shown that the task switcher manages +900 cycles
|
||||
per second through the task list on a DigitalUnix machine and 50
|
||||
cycles per second on a pentium 133mhz machine running linux. Both data
|
||||
were obtained with software simulation of hardware devices. With real
|
||||
SINQ hardware these numbers drop 4 cycles per second. This shows
|
||||
clearly that the communication with the hardware is the systems
|
||||
bottleneck and not the task switching scheme.
|
||||
|
||||
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
% Copyleft (c) 1997-2000 by Mark Koennecke at PSI, Switzerland.
|
||||
%
|
||||
% major upgrade: Mark Koennecke, July 2003
|
||||
%
|
||||
%
|
||||
|
||||
@ -31,7 +32,18 @@
|
||||
\include{overview}
|
||||
\include{proto}
|
||||
\include{kernelguide}
|
||||
\include{oguide}
|
||||
|
||||
|
||||
\include{command}
|
||||
%%\include{oguide}
|
||||
\include{sicsdriver}
|
||||
\include{site}
|
||||
\end{document}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,36 +1,33 @@
|
||||
\chapter{The SICS Server Client Protocol}
|
||||
This short chapter describes the command protocol between the SICS server
|
||||
and possible SICS clients. All this is very simple.
|
||||
|
||||
\section{Logging in to the SICS Server}
|
||||
In order to log in to the SICS server it needs to be known on which
|
||||
machine the server runs and at which port number the server listens for
|
||||
connection requests. Also needed is a valid username/ password pair for the
|
||||
SICS server in question. Given that the procedure for connecting to a SICS
|
||||
server requires the following steps:
|
||||
\begin{enumerate}
|
||||
\item Open a TCP/IP connection to the SICS server port at the machine
|
||||
where it is running.
|
||||
\item Immediately after opening the connection send the username/password
|
||||
pair. If everything is OK, a string OK is sent. Else the server will break
|
||||
the connection again.
|
||||
\end{enumerate}
|
||||
|
||||
\section{Sending Commands}
|
||||
After login, two means of communications exist. The communication
|
||||
protocoll is choosen through the server port the client connects too.
|
||||
The recommended way is
|
||||
to adhere to the telnet protocoll as described in RFC-854. Just a
|
||||
basic NVT (Network Virtual Terminal) with no options is
|
||||
implemented. Binary communication is not possible on a telnet port.
|
||||
|
||||
The older way of communication is to send commands directly on the
|
||||
TCP/IP port. Commands are strings terminated by a \verb+\n+. Return
|
||||
messages from the server have the same format. This scheme is
|
||||
obsolete but it has been left in because the need for a binary
|
||||
communication may arise and this would help implement such a thing.
|
||||
The SICS server actually listens for connections on two sockets, each
|
||||
implementing a different protocoll. The first type of connection
|
||||
implements the telnet protocoll. The second type uses a plain socket
|
||||
and has the advantage that binary data can be transferred.
|
||||
|
||||
\section{Connecting using Telnet}
|
||||
The SICS server implements the most primitive telnet server possible
|
||||
and does not support any of the fancy options possible with
|
||||
telnet. Using the telnet protocoll involves:
|
||||
\begin{itemize}
|
||||
\item Open a scoket conenction to SICS telnet port
|
||||
\item Send a login word followed by a username and a password. The
|
||||
login word is set in SICS initialization file as the SicsOption
|
||||
TelWord.
|
||||
\item On success a welcome message is printed, otherwise SICS
|
||||
terminates the connection.
|
||||
\item Now commands can be sent, but watch for the telnet protocoll
|
||||
specification in RFC-?????.
|
||||
\end{itemize}
|
||||
|
||||
\section{Connection using a plain Connection}
|
||||
This protocoll involves:
|
||||
\begin{itemize}
|
||||
\item Open a scoket conenction to SICS telnet port
|
||||
\item Send a username and a password.
|
||||
\item On success OK is printed, otherwise SICS
|
||||
terminates the connection.
|
||||
\item Now commands can be sent as strings terminated with a newline.
|
||||
\end{itemize}
|
||||
|
||||
For a list of possible commands consult the
|
||||
user documentation.
|
||||
@ -59,3 +56,6 @@ ASCII string of the form: {\bf SICSINT num} must be sent. num must be
|
||||
replaced by the number of the interrupt to issue. Again interrupt codes are
|
||||
resolved in file interrupt.h.
|
||||
|
||||
|
||||
|
||||
|
||||
|
620
doc/programmer/sicsdriver.tex
Normal file
620
doc/programmer/sicsdriver.tex
Normal file
@ -0,0 +1,620 @@
|
||||
\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.
|
||||
|
||||
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.
|
||||
\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.
|
||||
\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 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 reprsented 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
|
||||
elvel 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 interrupted 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 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[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 repsonse 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 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[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 (or mood?).
|
||||
\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}
|
||||
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 retun 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 error will be filled with a text description
|
||||
of the error. But maximum iLen 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 COFAIL 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 betwen 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 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. 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 retun 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] is 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
|
||||
komma. 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 seelctor. 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 seelctors 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 retun 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}
|
129
doc/programmer/site.tex
Normal file
129
doc/programmer/site.tex
Normal file
@ -0,0 +1,129 @@
|
||||
\chapter{Site Adaptions}\label{site}
|
||||
Any new site adapting SICS will have different hardware and thus
|
||||
require different drivers. Moreover additional commands may need to be
|
||||
added in order to support special hardware, instrument specific
|
||||
computations or status displays and local usage patterns. In order to
|
||||
separate such site specific code from the SICS kernel, the site data
|
||||
structure was conceived. Any new site is supposed to create a library
|
||||
which provides site specific code and the site data structure which
|
||||
allows SICS to locate the code. A site data structure can be retrieved
|
||||
using:
|
||||
\begin{verbatim}
|
||||
pSite getSite(void);
|
||||
\end{verbatim}
|
||||
The site data structure is meant to be a singleton. It is a site's
|
||||
programmers task to provide an implementation of getSite which returns
|
||||
a nice site structure.
|
||||
|
||||
The site data structure is a structure which holds pointers to
|
||||
functions. A user has to implement suitable functions along the
|
||||
signatures given and assign them to this data structure.
|
||||
\begin{verbatim}
|
||||
typedef struct {
|
||||
void (*AddSiteCommands)(SicsInterp *pSics);
|
||||
void (*RemoveSiteCommands)(SicsInterp *pSics);
|
||||
pMotor (*CreateMotor)(SConnection *pCon,
|
||||
int argc, char *argv[]);
|
||||
pCounterDriver (*CreateCounterDriver)(
|
||||
SConnection *pCon,
|
||||
int argc,
|
||||
char *argv[]);
|
||||
HistDriver *(*CreateHistogramMemoryDriver)(
|
||||
char *name, pStringDict pOption);
|
||||
pVelSelDriv (*CreateVelocitySelector)(char *name,
|
||||
char *array, Tcl_Interp *pTcl);
|
||||
pCodri (*CreateControllerDriver)(SConnection *pCon,
|
||||
int argc,
|
||||
char *argv[]);
|
||||
pEVControl (*InstallEnvironmentController)(
|
||||
SicsInterp *pSics,
|
||||
SConnection *pCon,
|
||||
int argc,
|
||||
char *argv[]);
|
||||
int (*ConfigureScan)(pScanData self,
|
||||
char *option);
|
||||
void (*KillSite)(void *pData);
|
||||
}Site, *pSite;
|
||||
\end{verbatim}
|
||||
|
||||
The members of this data structure:
|
||||
\begin{description}
|
||||
\item[AddSiteCommand] adds site specific commands coded in C to the
|
||||
SICS interpreter pSics.
|
||||
\item[RemoveSiteCommands] removes object creation commands after SICS
|
||||
has processed the instrument initialization file. See \ref{factory}
|
||||
for details on the scheme.
|
||||
\item[CreateMotor] creates a motor object. \verb+argv[0]+ contains the
|
||||
motors name, \verb+argv[1]+ the identifier for the motor driver and
|
||||
the rest of argv, argc holds further driver initialisation
|
||||
parameters. Any errors in processing the arguments can be reported to
|
||||
pCon. If CreateMotor can create a suitable motor object, a pointer to
|
||||
it is returned, if not NULL must be returned.
|
||||
\item[CreateCounterDriver] creates a driver for a counter. argc, argv
|
||||
is the full array of arguments given to the MakeCounter factory
|
||||
function. Of interest are: \verb+argv[1]+ the counter name,
|
||||
\verb+argv[2]+, the driver identifier and the rest of the
|
||||
initialization arguments. On success a pointer to
|
||||
new driver is returned, on failure NULL.
|
||||
\item[CreateHistogramMemoryDriver] creates a driver for a histogram
|
||||
memory. The driver is identified through name, the options database is
|
||||
in pOptions. Histogram memory initialization follows the following
|
||||
pattern:
|
||||
\begin{itemize}
|
||||
\item At first the raw driver is created. This code has to initializie
|
||||
defaults in the options data base.
|
||||
\item Then, with calls to {\em hmname configure opt val} the options
|
||||
database is populated with the histogram memories configuration
|
||||
options. The options database is pOptions a dictionary of name value
|
||||
pairs.
|
||||
\item In the last step, with {\bf hmname init} the options are parsed
|
||||
and the driver is supposed to connect to the histogram memory. See
|
||||
Configure in the histogram memory driver.
|
||||
\end{itemize}
|
||||
On success a pointer to
|
||||
new driver is returned, on failure NULL.
|
||||
\item[CreateVelSelDriv] creates a driver for a velocity selector. The
|
||||
driver is identified by nname, array is the name of a Tcl array in
|
||||
pTcl holding initialization parameters for name.
|
||||
\item[MakeController] generates a driver for a SICS general controller
|
||||
object. \verb+argv[0]+ is the driver identifier, the rest of argc,
|
||||
\verb+argv[]+ are further initialization parameters. Any errors in
|
||||
parsing argc, argv can be reported to pCon. On success a pointer to
|
||||
new driver is returned, on failure NULL.
|
||||
\item[InstallEnvironmentController] installs a sample environment
|
||||
controller into pSics. \verb+argv[3]+ is the driver indentifier,
|
||||
\verb+argv[2]+ is the SICS name of the environment device command, the
|
||||
rest are initialization parameters. This function must also install
|
||||
the command into pSics with AddCommand. This is because for many PSI
|
||||
environment devices special interpreter wrapper functions are
|
||||
provided. Any errors encountered while processing the arguments has to
|
||||
be reported to pCon. On success a pointer to the environment
|
||||
controller is returned, on failure NULL.
|
||||
\item[ConfigureScan] configures the SICS general scan object self according
|
||||
to the value of option. Returns 1 on success and 0 on failure. SICS
|
||||
general scan object is a data structure holding function pointers for
|
||||
various steps in the scan. These functions can be overloaded in order
|
||||
to provide for special scans. See the documentation ins scan.tex,
|
||||
scan.h and scan.c from more details.
|
||||
\end{description}
|
||||
|
||||
|
||||
All the simulation drivers for the hardware are part of the SICS
|
||||
kernel and need not be initialized from these functions. SICS also
|
||||
handles sample environment devices built in Tcl or on the general
|
||||
controller object.
|
||||
|
||||
|
||||
The site data structure suffers a little from inconsistencies
|
||||
introduced through varying concepts for initializing SICS objects introduced
|
||||
during the development of SICS. If you need to bypass the schemes
|
||||
introduced here, consider implementing an own factory command and
|
||||
install it through AddSiteCommand, RemoveSiteCommand.
|
||||
|
||||
|
||||
|
||||
Good luck!
|
||||
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user