Initial revision
This commit is contained in:
396
interface.w
Normal file
396
interface.w
Normal file
@ -0,0 +1,396 @@
|
||||
\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>
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
int (*SaveStatus)(void *self, char *name,FILE *fd);
|
||||
void *(*GetInterface)(void *self, int iInterfaceID);
|
||||
} ObjectDescriptor, *pObjectDescriptor;
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
pObjectDescriptor CreateDescriptor(char *name);
|
||||
void DeleteDescriptor(pObjectDescriptor self);
|
||||
|
||||
/*============================================================================
|
||||
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;
|
||||
} IDrivable, *pIDrivable;
|
||||
|
||||
pIDrivable GetDrivableInterface(void *pObject);
|
||||
|
||||
@}
|
||||
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.
|
||||
|
||||
\subsubsection{The Countable Interface}
|
||||
This is an interface for interacting with anything which counts.
|
||||
|
||||
|
||||
@d count @{
|
||||
typedef struct {
|
||||
int ID;
|
||||
int (*Halt)(void *self);
|
||||
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);
|
||||
|
||||
@}
|
||||
|
||||
{\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.
|
||||
|
||||
\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);
|
||||
@}
|
||||
|
||||
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.
|
||||
|
||||
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@>
|
||||
@}
|
||||
|
||||
@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
|
||||
|
||||
/* 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.
|
||||
|
||||
|
Reference in New Issue
Block a user