- 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:
cvs
2003-06-30 11:51:35 +00:00
parent 007a2e2536
commit e52bd5d937
17 changed files with 1561 additions and 3655 deletions

637
doc/programmer/command.tex Normal file
View 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.

View File

@ -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.

View File

@ -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

View File

@ -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.

View File

@ -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}

View File

@ -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.

View 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
View 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!