Files
sics/devexec.w
Ferdi Franceschini 10d29d597c Cleaned up ANSTO code to merge with sinqdev.sics
This is our new RELEASE-4_0 branch which was taken from ansto/93d9a7c
Conflicts:
	.gitignore
	SICSmain.c
	asynnet.c
	confvirtualmot.c
	counter.c
	devexec.c
	drive.c
	event.h
	exebuf.c
	exeman.c
	histmem.c
	interface.h
	motor.c
	motorlist.c
	motorsec.c
	multicounter.c
	napi.c
	napi.h
	napi4.c
	network.c
	nwatch.c
	nxscript.c
	nxxml.c
	nxxml.h
	ofac.c
	reflist.c
	scan.c
	sicshipadaba.c
	sicsobj.c
	site_ansto/docs/Copyright.txt
	site_ansto/instrument/lyrebird/config/tasmad/sicscommon/nxsupport.tcl
	site_ansto/instrument/lyrebird/config/tasmad/taspub_sics/tasscript.tcl
	statusfile.c
	tasdrive.c
	tasub.c
	tasub.h
	tasublib.c
	tasublib.h
2015-04-23 20:49:26 +10:00

336 lines
13 KiB
OpenEdge ABL

\subsection{The Device Executor}
The Device Executor (devexec) is a core component of the system. It has to
fulfill three main tasks:
\begin{itemize}
\item Permit non--blocking hardware operations.
\item Ensure regular monitoring of running devices.
\item Ensure that only one client controls the hardware.
\end{itemize}
The devexec in its current form monitors driving and counting
operations only. The emonitor implements another monitor for
environment controllers.
Please note, that this module is quite crucial for the functioning of
SICS. Any changes here may have side effects throughout the whole
system. Be VERY careful with any changes. The current version does its job!
Some users want to continue typing commands while some hardware device is
still running. This is sensible, because some hardware devices require a
lot of time before they run to completion. Some people also require to count
while driving motors for quick overview measurements. This requirement was
the main reason for the invention of the devexec.
Of course, when devices are in operation it is needed to check on them
regularly in order to catch and report error conditions and in order to
find out when devices are finished with their job.
In a client server system many clients might issue commands to the hardware.
This could quickly lead into an undesirable form of chaos (There are
desirable forms of chaos, but not here!). In order to prevent this a means
is needed to ensure that at any given time only one client controls the
hardware. This function is also performed by the devexec.
The device executor also has to take care of special error conditions.
\subsubsection{Starting Devices}
These are the most important SICS operations. Environment controllers are
monitored by the environment monitor. Then here is a convention: {\bf Any
SICS object which
initiates driving or counting operations has to do so by registering this
operation with the devexec}. For this purpose the following interface
functions are provided.
@d devreg @{
int StartDevice(pExeList self, char *name, pObjectDescriptor pDes,
void *pData, SConnection *pCon, int level, float fNew);
int StartMotor(pExeList self, SicsInterp *pSics, SConnection *pCon,
char *name, int level, float fNew);
int StartCounter(pExeList self, SicsInterp *pSics, SConnection *pCon,
int level, char *name);
@}
The main interface function is {\bf StartDevice}. The parameters are:
\begin{itemize}
\item {\bf self}. A pointer to the device executor in which SICS operates.
\item {\bf name}. The name of the object which operates.
\item {\bf pDes}. A pointer to the ObjectDescriptor of the object to drive or count.
\item {\bf pData}. A pointer to the data structure coming with the object.
\item {\bf level} The start level of the device.
\item {\bf pCon}. A pointer to the client connection on whose request the
operation was initiated.
\item {\bf fNew}. A floating point value which sets the target value for
drivable devices.
\end{itemize}
{\bf StartMotor, StartCounter} are just convenience wrappers around
StartDevice which retrieve objects from the SICS interpreter and calls
StartDevice thereafter.
Once invoked StartDevice takes care of the following operations:
\begin{itemize}
\item It first checks on the connection object. If nobody else is running
hardware it enters the connection object specifed as owner of the devexec in
its data structure. If an owner was already specified by a prior drive or
count request StartDevice checks if the connection requesting the new
operation is the same. If this is not the case, an error message about this
situation will be issued. If it is the case, i. e. the client requesting the
new operation is the same as the one who has reserved the devexec, the
operation is performed. This scheme reserves the devexec to one client.
\item If the authorisation is OK, StartDevice then proceeds to start the
drive or counting operation.
\item StartDevice then enters all information regarding
the running device into an list for future monitoring.
\item If it not already running a DevExecTask is registered with the
task module in order to ensure the monitoring of the devices running.
\end{itemize}
\subsubsection{Monitoring devices}
From within the SICS main loops this special function is called:
@d devcheck @{
int CheckExeList(pExeList self);
/*
checks the entries for success and deletes entries which have finished
operation. If there are none left, the pOwner will be set to NULL.
*/
int Wait4Success(pExeList self);
long GetDevexecID(pExeList self);
int DevExecLevelRunning(pExeList self, int level);
int DevExecTask(void *pEL);
void DevExecSignal(void *pEL, int iSignal, void *pSigData);
int GetDevExecInstStatus(pExeList self);
@}
CheckExeList then scan through its list of executing objects and request a
status from each of them. The next action depend on the status returned from
the device and any pending interrupts:
\begin{itemize}
\item If the device is still busy and no interrupts are pending, nothing
happens.
\item If there is a hardware error, this will be reported and apropriate
actions are intitiated depending on the type of problem and possible
interrupts being sets.
\item If a device is done, either naturally or due to an error or interrupt,
it is removed from devexec's list.
\item If the list is empty, the owner field of the devexec's datastructure
is reset to NULL. Then a new client is free to grab control over the
hardware.
\end{itemize}
{\bf DevExecTask} is the task function for the device executor. Calls
CheckExeList in the end. If all devices registered with the devexec
are finished running this function returns 0 and thus stops.
{\bf DevExecSignal} is the signal function for the device executor task.
{\bf Wait4Success} This function waits for the DevExecTask to
end. This is the case when the current devices running are finished.
There are occasions in the program where it is needed to wait for
an operation to complete before other tasks can be tackled. Wait4Success is
the function to call in such cases. Wait4Success returns DEVDONE for a
properly finished operation, DEVERROR for an operation which finished
with an error code and DEVINT for an aoperation which was interrupted
by the user.
{\bf DevExeclevelRunning} tests if the level given as a parameter is still running.
\subsubsection{Influencing Execution}
In certain cases it is necessary to interact with running devices directly.
This is done via the following interface.
@d devstop @{
int StopExe(pExeList self, char *name);
int StopExeWait(pExeList self);
/*
will stop the entry name and its subentries from executing.
If ALL is specified as name, everything will be stopped and
the Executor cleared.
StopExeWait will stop all running things and wait for the stop
to complete.
*/
int StopByData(pExeList self, void *data);
/* stop the entry with the given data from execution */
/*------------------------------------------------------------------------*/
void ClearExecutor(pExeList self);
/*
clears the executor without sending commands to the devices.
*/
/*-----------------------------------------------------------------------*/
int IsCounting(pExeList self);
int PauseExecution(pExeList self);
int ContinueExecution(pExeList self);
@}
{\bf StopExe} tackles the interrupting of pending operations. This may
happen on a users request. StopExe then invokes a halt on that device.
As parameters, the name of a device to stop can be specified. If ALL is
given as name to StopExe all executing devices are stopped. Please note, that
this stop does not automatically finish the operation. Motors need some time
to stop, too. This function is usally called as consequence of an
interrupt.
{\bf ClearExecutor} clears the executor without invoking commands on the
devices. Is probably only used internally to clean out the executor.
I some cases, for example if a environment controller gets out of range, an
error handling strategy may want to pause counting until the problem has
been rectified and continue afterwards. {\bf PauseExecution, ContinueExecution}
take care of invoking the apropriate commands on all registered counting
devices.
\subsubsection{Locking the Device Executor}
In some instances user code may wish to lock the device executor. An
example is a long running data saving operation. In order to do this
two functions are provided:
@d devlock @{
void LockDeviceExecutor(pExeList self);
void UnlockDeviceExecutor(pExeList self);
@}
\subsubsection{The Rest}
The rest of the interface includes initialisation and deletion routines
and some access routines. With the devexec being such an important system
component a function {\bf GetExecutor} is provided which retrieves a pointer
to the global SICS device executor.
@o devexec.h -d @{
/*----------------------------------------------------------------------------
D E V I C E E X E C U T O R
Joachim Kohlbrecher wants to give several commands to the server
and than wait for them to happen before proceeding. Actually a useful
thing. A command will map to one or several positioning commands for a
device. This scheme is also useful for implementing objects which
drive several motors simulataneously, such as Monochromators. etc.
However, the whole thing is rather complicated.
It is forbidden, that several clients drive the instrument. In order
to ensure this the Executor remembers the connection which emitted the
first command. Subsequent AddExeEntries from different clients will
be rejected. The owner will be reset when the execution is found finished
in calls to CheckExe, Wait4Success or StopExe.
Mark Koennecke, December 1996
copyright: see implementation file
---------------------------------------------------------------------------*/
#ifndef SICSDEVEXEC
#define SICSDEVEXEC
#include "obdes.h"
#include "task.h"
typedef struct __EXELIST *pExeList;
/* Returncodes */
#define DEVDONE 1
#define DEVINT 0
#define DEVERROR 2
#define DEVBUSY 3
/* run level codes */
#define RUNRUN 0
#define RUNDRIVE 1
/*------------------------------------------------------------------------
B I R T H & D E A T H
*/
pExeList CreateExeList(pTaskMan pTask);
void DeleteExeList(void *self);
/* ================= Functions to talk to the above ====================== */
@<devreg@>
/*------------------------------------------------------------------------*/
@<devcheck@>
/*
Waits for execution to finish. returns 1 on Success, 0 if problems
ocurred. Than the Interrupt code shall be checked and acted upon
accordingly.
*/
/*-------------------------------------------------------------------------*/
SConnection *GetExeOwner(pExeList self);
/*-------------------------------------------------------------------------*/
int isInRunMode(pExeList self);
/*--------------------------------------------------------------------------*/
int ListPending(pExeList self, SConnection *pCon);
/*
lists the Operations still pending on pCon.
*/
/*-------------------------------------------------------------------------*/
@<devstop@>
/*-------------------------- Commands ------------------------------------*/
int DevexecAction(SConnection *pCon, SicsInterp *pSics, void *pData,
int argc, char *argv[]);
int StopCommand(SConnection *pCon, SicsInterp *pSics, void *pData,
int argc, char *argv[]);
/*
implements the stop command
*/
int ListExe(SConnection *pCon, SicsInterp *pSics, void *pData,
int argc, char *argv[]);
/*
lists all currently executing objects
*/
int SicsIdle(SConnection *pCon, SicsInterp *pSics, void *pData,
int argc, char *argv[]);
/*
prints the seconds since the device executor was running the last time
*/
int Success(SConnection *pCon, SicsInterp *pSics, void *pData,
int argc, char *argv[]);
/*
waits until completion of all pending operations. Used in
connection with non blocking operation such as motors started
with run.
*/
int PauseAction(SConnection *pCon, SicsInterp *pSics, void *pData,
int argc, char *argv[]);
/*
pauses execution
*/
int ContinueAction(SConnection *pCon, SicsInterp *pSics, void *pData,
int argc, char *argv[]);
/*
continues execution
*/
/*--------------------------- Locking ---------------------------------*/
@<devlock@>
/* -------------------------- Executor management -------------------------*/
pExeList GetExecutor(void);
void SetExecutor(pExeList pExe);
/**
* This is only used in sicshdbadapter.c
* It became a void pointer because of circular dependencies in the
* header files.
*/
void *GetExecutorCallback(pExeList self);
/*----------------------- Logging -----------------------------------------*/
void DevexecLog(char *op, char *device);
void ExeInterest(pExeList pExe, char *name, char *action);
void InvokeNewTarget(pExeList pExe, char *name, float fTarget);
void SetDevexecStatus(pExeList pExe, int code);
#endif
@}