
- Extended tasker to support task groups - Added task functions for motors and counters - Modifed devexec to use the new task functions - Modified TAS to treat the monochromator separatly - Coded a EIGER monochromator module to reflect even more new requirements - Added EPICS counters and motors - Modified multicounter to be better performing SKIPPED: psi/eigermono.c psi/make_gen psi/makefile_linux psi/psi.c psi/sinqhttp.c
467 lines
20 KiB
OpenEdge ABL
467 lines
20 KiB
OpenEdge ABL
\subsection{Object interfaces}\label{inter}
|
|
In order to present themselves to the system SICS objects need to adhere to
|
|
certyain interfaces. These interfaces are described in this
|
|
section. Thus this section is one of the most important sections of
|
|
theis document, read carefully!
|
|
|
|
A first
|
|
requirement was that it must be possible to inquire the capabilities of an
|
|
object at run time. A second one was that the system should be extendable.
|
|
This means it should be possible to add new interfaces or modify them as the
|
|
need arises. In order to achieve this the idea of the interface
|
|
datastructure (INDS) was conceived. An INDS contains all the variables and
|
|
functions which make up an interface. Then it is possible to create such a
|
|
datastructure and pass it around. Each object can now be requested to pass
|
|
an INDS of a certain type on demand. What is still needed is a standard way
|
|
of asking a SICS object for such a datastructure. This is achieved by the
|
|
means of the object descriptor. This is a datastructure which contains
|
|
everything necessary to identify an object to SICS. This requires a
|
|
convention which is that {\em each SICS objects has to have a pointer to an
|
|
object descriptor as first element of its own datastructure.} All this
|
|
probably gets clearer once the actual datastructures have been discussed.
|
|
|
|
|
|
As usual, functions return 1 on success and 0 on failure.
|
|
|
|
\subsubsection{The object descriptor}
|
|
Let's start with the objectdescriptor:
|
|
|
|
@d obdes @{
|
|
/*--------------------------------------------------------------------------
|
|
In SICS there is the to find out what an
|
|
object is capable of at runtime. If this has been done a general
|
|
way to access those capabilities is needed. In order to do all
|
|
this each SICS-object is required to carry an object descriptor
|
|
struct as first parameter in its class/object struct. Additionslly
|
|
it is required to initialize this struct to something sensible.
|
|
|
|
This file defines this struct. Additionally a few functions of
|
|
general use are prototyped.
|
|
|
|
Mark Koennecke, June, 1997
|
|
|
|
copyrigth: see implementation file
|
|
----------------------------------------------------------------------------*/
|
|
#ifndef SICSDESCRIPTOR
|
|
#define SICSDESCRIPTOR
|
|
#include <stdio.h>
|
|
#include <ifile.h>
|
|
#include <hipadaba.h>
|
|
|
|
typedef struct {
|
|
char *name;
|
|
int (*SaveStatus)(void *self, char *name,FILE *fd);
|
|
void *(*GetInterface)(void *self, int iInterfaceID);
|
|
IPair *pKeys;
|
|
pHdb parNode;
|
|
} ObjectDescriptor, *pObjectDescriptor;
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
pObjectDescriptor CreateDescriptor(char *name);
|
|
void DeleteDescriptor(pObjectDescriptor self);
|
|
pObjectDescriptor FindDescriptor(void *pData);
|
|
|
|
/*============================================================================
|
|
Objects which do not carry data need a dummy descriptor. Otherwise
|
|
drive or scan will protection fault when trying to drive something
|
|
which should not be driven. This is defined below.
|
|
*/
|
|
|
|
typedef struct {
|
|
pObjectDescriptor pDescriptor;
|
|
}Dummy, *pDummy;
|
|
|
|
|
|
pDummy CreateDummy(char *name);
|
|
void KillDummy(void *pData);
|
|
|
|
int iHasType(void *pData, char *Type);
|
|
|
|
#endif
|
|
@}
|
|
|
|
The first element of the object descriptor is a {\bf name} which is better
|
|
described as a type identifier. This specifies the type of the object.
|
|
|
|
|
|
The function {\bf SaveStatus} will be called automatically by SICS when the
|
|
server is closing down or a status backup is requested. Each object is meant
|
|
to print the commands necessary to configure it back into its current state
|
|
into the file passed as a parameter. The idea is that by executing the file
|
|
thus created the system gets back into the state before closedown.
|
|
|
|
The heart of the interface system is the {\bf GetInterface}
|
|
function. It takes as arguments a pointer to the datastructure on which it
|
|
is called and an integer ID
|
|
for the interface. These ID's are defined in the file interface.h. This
|
|
function is meant to return a pointer to the apropriate interface
|
|
datastructure after this call. If the object does not implement the interface
|
|
requested, this function should return NULL. New interfaces can be added
|
|
into the scheme by defining new ID's, interfaces and objects which implement
|
|
them.
|
|
|
|
It is {\bf important} to note, that the objects themselves are responsible
|
|
for allocating and freeing memory for the interface structures. Client never
|
|
should need to worry how to dispose of these structures. Moreover this
|
|
scheme ensures that changes to the interface due to some command given to
|
|
the object are immediatetly visible through the whole system.
|
|
|
|
Additionally this header file defines a few relatively uninteresting
|
|
functions for object descriptor maintainance. Slightly more interesting is
|
|
the Dummy structure, which will be used to find the object descriptor in a
|
|
given objects data structure.
|
|
|
|
|
|
\subsubsection{The drivable interface}
|
|
As first example of an interface the drivable interface will be given. This
|
|
interface is implemented by all devices or varaibles which can be driven to
|
|
a value. Most notable example are motors, but composite variables and
|
|
environment controllers fit this bill as well.
|
|
|
|
@d driv @{
|
|
|
|
typedef struct {
|
|
int ID;
|
|
int (*Halt)(void *self);
|
|
int (*CheckLimits)(void *self, float fVal,
|
|
char *error, int iErrLen);
|
|
long (*SetValue)(void *self, SConnection *pCon,
|
|
float fVal);
|
|
int (*CheckStatus)(void *self, SConnection *pCon);
|
|
float (*GetValue)(void *self, SConnection *pCon);
|
|
int iErrorCount;
|
|
int drivableStatus;
|
|
} IDrivable, *pIDrivable;
|
|
|
|
pIDrivable GetDrivableInterface(void *pObject);
|
|
int GetDrivablePosition(void *pObject, SConnection *pCon,
|
|
float *fPos);
|
|
|
|
long StartDriveTask(void *self, SConnection *pCon, char *name, float fTarget);
|
|
|
|
@}
|
|
The first member of this structure is an ID which can be used in order to
|
|
check if the right datastructure has been obtained.
|
|
|
|
The second field is a pointer to a {\bf Halt} function. This function will be
|
|
called in an emergency and is meant to send a stop command to the device.
|
|
|
|
The third field is a pointer to the {\bf CheckLimits} function. This function is
|
|
given a float value as second parameter. This is the new value the device
|
|
should go to. CheckLimits checks if this position is permitted. If so it
|
|
returns 1 else 0.
|
|
|
|
The {\bf SetValue} member is a pointer to a function which actually makes the
|
|
object start to move to its new position.
|
|
|
|
The {\bf CheckStatus} function will be called in order to check for the current
|
|
status of the device during operation. Possible return values are:
|
|
\begin{itemize}
|
|
\item OKOK if everything is OK.
|
|
\item HWIdle if the device is idle i. e. doing nothing.
|
|
\item HWFault if the device is in trouble.
|
|
\item HWBusy if the device is still running.
|
|
\item HWPosFault when the device is principally OK but has not reached the
|
|
position specified, for instance because somebody has put a slab of concrete
|
|
into the instruments way.
|
|
\end{itemize}
|
|
|
|
The last function which makes up this interface is {\bf GetValue} which is meant
|
|
to return the current position of the device.
|
|
|
|
{\bf iErrorCount} is the number of number of failed driving commands on this drivable
|
|
item. This can be used by motors to check and abort the experiment if there
|
|
are to many errors. This is to stop a scan command hammer at a blocked motor
|
|
a hundred times.
|
|
|
|
{\bf GetDrivableInterface} is a convenience function which checks object pData for
|
|
the existence of a drivable interface. If it exists a pointer to it will be
|
|
returned. NEVER free this pointer. If no drivable interface exists, NULL
|
|
will be returned.
|
|
|
|
{\bf GetDrivablePosition retrieves the position of the drivabel
|
|
object. If the device is a motor corrections for zero points and signs
|
|
will be applied. Returns 1 on success and 0 on failure}
|
|
|
|
{\bf StartDriveTask starts a drivable in a new task. If the task can be started, its
|
|
task ID is returned. -1 is returned on failure.
|
|
}
|
|
|
|
|
|
\subsubsection{The Countable Interface}
|
|
This is an interface for interacting with anything which counts.
|
|
|
|
|
|
@d count @{
|
|
typedef struct {
|
|
int ID;
|
|
int running;
|
|
int (*Halt)(void *self);
|
|
void (*SetCountParameters)(void *self, float fPreset,
|
|
CounterMode eMode);\
|
|
int (*StartCount)(void *self, SConnection *pCon);
|
|
int (*CheckCountStatus)(void *self, SConnection *pCon);
|
|
int (*Pause)(void *self, SConnection *pCon);
|
|
int (*Continue)(void *self, SConnection *pCon);
|
|
int (*TransferData)(void *self, SConnection *pCon);
|
|
} ICountable, *pICountable;
|
|
|
|
pICountable GetCountableInterface(void *pObject);
|
|
int GetCountLock(pICountable self, SConnection *pCon);
|
|
void ReleaseCountLock(pICountable self);
|
|
int isRunning(pICountable self);
|
|
long StartCountTask(void *self, SConnection *pCon, char *name);
|
|
@}
|
|
|
|
{\bf running } Is a flag which says if the counter is operating or not.
|
|
|
|
{\bf Halt and StartCount} are self explaining, they just do what they say. Please
|
|
note, that counting configuration must have happened before usage of this
|
|
interface.
|
|
|
|
{\bf Pause} pauses a data aquisition. It does NOT abort it. {\bf Continue}
|
|
continues a paused data aquisition.
|
|
|
|
{\bf CheckCountStatus} works much like CheckStatus in the IDrivable interface
|
|
described above. However, there is an additional return code: HWNoBeam. This
|
|
is retuned if a monitor is below a certain threshold, i. e. the beam is off.
|
|
|
|
{\bf TransferData} will be called at the end of each counting operation and is
|
|
meant to update the internal datastructures of the counter. Some counters
|
|
choose to keep the last count in their own datastructure, some such as
|
|
histogram memories might choose to leave them in the hardware. This function
|
|
has been conceived in order to ensure that the first type of counters is
|
|
supported.
|
|
|
|
{\bf GetCountableInterface} is a convenience function which checks object pData for
|
|
the existence of a countable interface. If it exists a pointer to it will be
|
|
returned. NEVER free this pointer. If no countable interface exists, NULL
|
|
will be returned.
|
|
|
|
{\bf GetCountLock} will try to set the running flag. If it is already running, an error
|
|
is printed to pCon and 0 is returned.
|
|
|
|
{\bf ReleaseCountLock} release the count lock.
|
|
|
|
{\bf isRunning} returns the running flag.
|
|
|
|
{\bf StartCountTask starts a countable in a new task. If the task can be started, its
|
|
task ID is returned. -1 is returned on failure.
|
|
}
|
|
|
|
|
|
\subsubsection{The Callback Interface}
|
|
The Callback Interface is SICS suport for component behaviour for objects.
|
|
Consider objects A and B. A now is able to generate certain events when it's
|
|
state changes. For instance if A is a variable that its value is changed.
|
|
B may then choose to register a function with A which gets automatically
|
|
called whenever A generates the apropriate event. B is thus automatically
|
|
notified about A's status change and can act accordingly to it. In contrast
|
|
to the interfaces defined above, this interface is defined in terms of a set
|
|
of functions which manipulate the interface and not as a data structure of
|
|
overloadable functions.
|
|
|
|
The first thing to define for such an interface is the type of the callback
|
|
function:
|
|
@d callfunc @{
|
|
typedef void (*KillFuncIT)(void *pData);
|
|
typedef int (*SICSCallBack)(int iEvent, void *pEventData,
|
|
void *pUserData);
|
|
@}
|
|
|
|
The callback function is meant to return 0 for failure or 1 for success.
|
|
This infomation may be needed by an event invoking object if to continue an
|
|
operation or not. The first parameter passed to {\bf SICSCallBack} is the id of
|
|
the generated event. Clearly the communicating objects need to agree on
|
|
these event. In SICS events types will be held in an header file event.h.
|
|
The next parameter to SICSCallBack is a pointer to void. The event
|
|
generating object may choose to pass additional data along with the event.
|
|
Suitable event datastructures are also defined in event.h. The last
|
|
parameter to SICSCallBack is a pointer to a datastructure defined by the
|
|
owner of the callback routine when registering the callback. {\bf pKill} is a
|
|
function which will be called automatically when the callback is deleted and
|
|
whose sole task is to delete pUserData properly.
|
|
|
|
Then there are the functions needed to interface with the callback
|
|
interface:
|
|
|
|
@d cifunc @{
|
|
typedef struct __ICallBack *pICallBack;
|
|
|
|
/* event source side */
|
|
pICallBack CreateCallBackInterface(void);
|
|
void DeleteCallBackInterface(pICallBack self);
|
|
int InvokeCallBack(pICallBack pInterface, int iEvent, void *pEventData);
|
|
|
|
/* callback client side */
|
|
long RegisterCallback(pICallBack pInterface,
|
|
int iEvent, SICSCallBack pFunc,
|
|
void *pUserData, KillFuncIT pKill);
|
|
int RemoveCallback(pICallBack pInterface, long iID);
|
|
int RemoveCallback2(pICallBack pInterface, void *pUserData);
|
|
int RemoveCallbackCon(pICallBack pInterface, SConnection *pCon);
|
|
|
|
int CallbackScript(SConnection *pCon, SicsInterp *pSics, void *pData,
|
|
int argc, char *argv[]);
|
|
|
|
pICallBack GetCallbackInterface(void *pData);
|
|
@}
|
|
|
|
The callback interface has two parts: A part the event generating object has
|
|
to deal with and a part the interested object has to use. There is no way of
|
|
enforcing this convention. However, SICS programmers should stick to it,
|
|
otherwise weird things may happen.
|
|
|
|
The first two functions just deal with creating and deleting the callback
|
|
interface data structure. {\bf CreateCallBackInterface} returns NULL if the
|
|
request fails, or a pointer on success.
|
|
|
|
|
|
{\bf InvokeCallBack} is responsible for invoking the registered callback functions
|
|
and will be called only by the source object for the event. This function
|
|
will scan the list of registered callback function for callback function
|
|
which are registered for the event ID specified as second parameter. Each of
|
|
them will be invoked with the event data given as third parameter. If any of
|
|
the callback functions returns Failure this function will do so as well.
|
|
Else 1 is returned. The event data structure belongs to the object invoking
|
|
the callback. It should never be deleted in callback functions.
|
|
|
|
The next functions deal with the client side of the callback interface.
|
|
|
|
{\bf RegisterCallBack} registers a callback function with the interface. The first
|
|
parameter is the interface to register the callback with. The second
|
|
parameter is the event ID for which this callback is defined. pFunc is the
|
|
pointer to the actual callback function. Finally pUserData is a pointer to
|
|
some user defined data structure which shall be passed as pUserData when the
|
|
callback is called. RegisterCallback retuns a long value which server as an
|
|
identifier for the callback registration. If the registration fails, this
|
|
function returns 0.
|
|
|
|
{\bf RemoveCallBack} is used to delete a registered callback. The first parameter
|
|
is the interface to act upon, the second is the callback ID as returned by
|
|
RegisterCallBack.
|
|
|
|
{\bf RemoveCallback2} is another variant for removing callbacks. This time the
|
|
search key for deletion is the pointer to user data. All callbacks related
|
|
to this user data in the interface specified will be removed.
|
|
|
|
{\bf RemoveCallbackCon} is another variant for removing callbacks. This time the
|
|
search key for deletion is the pointer to user data which must be a connection object.
|
|
All callbacks related to this connection in the interface specified will be removed. This is
|
|
a convenience function for removing interest callbacks in SICS.
|
|
|
|
{\bf CallbackScript} allows to connect callbacks to scripts. Please
|
|
note, that those scripts will have a dummy connection to clients only
|
|
and will not be able to write to clients. All output occurring in
|
|
these scripts will be directed to stdout though, in order to support
|
|
debugging.
|
|
|
|
|
|
All these functions are implemented in the file callback.c.
|
|
|
|
|
|
\subsubsection{The Environment Interface}
|
|
This interface is used by the environment monitor in order to monitor
|
|
the status of a environment controller. The interface looks like this:
|
|
@d envir @{
|
|
typedef enum { EVIdle, EVDrive, EVMonitor, EVError } EVMode;
|
|
typedef struct {
|
|
int iID;
|
|
EVMode (*GetMode)(void *self);
|
|
int (*IsInTolerance)(void *self);
|
|
int (*HandleError)(void *self);
|
|
} EVInterface, *pEVInterface;
|
|
@}
|
|
The enum {\bf EVMode} describes the mode a environment controller can be in. The
|
|
default is Idle, no action necessary. EVDrive means that the controller is
|
|
currently being driven to a new value and the device executor has control.
|
|
EVMonitor means that the environment monitor has to take care of the
|
|
controller. EVError is set if the controller is out of tolerances.
|
|
|
|
{\bf GetMode} retrieves the current operation mode of the controller.
|
|
|
|
{\bf IsInTolerance} returns 1 if the controller is still within tolerances,
|
|
0 otherwise.
|
|
|
|
{\bf HandleError} will be automatically called when IsInTolerance returns 0.
|
|
Its purpose is to implemnt the error handling startegy for the controller
|
|
in question.
|
|
|
|
|
|
The environment interface has just one function associated with it:
|
|
@d envfunc @{
|
|
pEVInterface CreateEVInterface(void);
|
|
@}
|
|
|
|
{\bf CreateEVInterface} just creates an environment interface.
|
|
|
|
@o obdes.h -d @{
|
|
@<obdes@>
|
|
/*--------------------------------------------------------------------------*/
|
|
/* Additional properties used by the ANSTO site to provide more information
|
|
* about each object instance, especially devices.
|
|
*/
|
|
void SetDescriptorKey(pObjectDescriptor self, char *keyName, char *value);
|
|
void SetDescriptorGroup(pObjectDescriptor self, char *group);
|
|
void SetDescriptorDescription(pObjectDescriptor self, char *description);
|
|
char * GetDescriptorKey(pObjectDescriptor self, char *keyName);
|
|
char * GetDescriptorGroup(pObjectDescriptor self);
|
|
char * GetDescriptorDescription(pObjectDescriptor self);
|
|
@}
|
|
|
|
@o interface.h -d @{
|
|
/*---------------------------------------------------------------------------
|
|
I N T E R F A C E S
|
|
|
|
Any object in SICS has to adhere to the object descriptor interface (see
|
|
file obdes.h). Furthermore SICS objects may choose to support other
|
|
interfaces as well. These interfaces are defined.
|
|
|
|
Mark Koennecke, June 1997
|
|
|
|
For more documentation see interface.w, interface.tex
|
|
|
|
copyright: see SICS impelementation files
|
|
---------------------------------------------------------------------------*/
|
|
|
|
#ifndef SICSINTERFACES
|
|
#define SICSINTERFACES
|
|
#include "commandcontext.h"
|
|
|
|
/* interface ID's used to recognize an interface */
|
|
#define DRIVEID 513
|
|
#define COUNTID 713
|
|
#define CALLBACKINTERFACE 947
|
|
#define ENVIRINTERFACE 949
|
|
|
|
/* ----------------------- The drivable interface -----------------------*/
|
|
@<driv@>
|
|
|
|
pIDrivable CreateDrivableInterface(void);
|
|
|
|
/* ------------------------ The countable interface ---------------------*/
|
|
@<count@>
|
|
|
|
pICountable CreateCountableInterface(void);
|
|
|
|
/* ------------------------- The CallBack Interface --------------------*/
|
|
@<callfunc@>
|
|
@<cifunc@>
|
|
/*---------------------- The Environment Interface --------------------*/
|
|
@<envir@>
|
|
@<envfunc@>
|
|
#endif
|
|
@}
|
|
|
|
|
|
\subsection{More interfaces}
|
|
For completeness it should be mentioned that besides these interfaces
|
|
there exist further important interfaces in SICS. This includes the
|
|
interface to the SICS interpreter defined in
|
|
\ref{scinter}. Furthermore the data structures defined for the various
|
|
types of drivers within SICS may be considered interfaces as
|
|
well. Then there is the interface to the client as defined by the
|
|
functions of the connection interface.
|
|
|
|
|