Files
sics/interface.w
koennecke 1afe142812 - Removed old code
- 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
2013-04-02 15:13:35 +00:00

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.