Initial revision
This commit is contained in:
209
doc/programmer/motor.tex
Normal file
209
doc/programmer/motor.tex
Normal file
@@ -0,0 +1,209 @@
|
||||
\subsection{Motors}
|
||||
Most neutron scattering instruments have lots of motors to drive
|
||||
monochromators, samples and detectors through space.This module implements
|
||||
support for them. As usual for hardware objects in SICS motors are
|
||||
subdivided into a driver and the logical object.
|
||||
|
||||
\subsubsection{The Motor Driver}
|
||||
There is a problem here. There are some data fields and functions
|
||||
which must be present for any motor driver. Then there are fields
|
||||
which are specific just to a special implementation of a mot
|
||||
driver. There are several ways to deal with this. The way choosen for
|
||||
the motor driver is a kind of overlay. The first few fields of a valid
|
||||
motor driver structure MUST be specified in the same order as given
|
||||
below. A special motor driver can add additional fields at the end of
|
||||
the standard list. As an example for this scheme compare the
|
||||
AbstractMoDriv structure with the one for the EL734 motor driver.
|
||||
\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 (*ContinueAfterWarn)(void *self);
|
||||
int (*Halt)(void *self);
|
||||
}
|
||||
MotorDriver;
|
||||
\end{verbatim}
|
||||
All functions return 0 on failure or 1 on success, if not stated otherwise.
|
||||
The first parameter is always a pointer to the motor driver structure.
|
||||
The fields and their meanings: \begin{description}
|
||||
\item[fUpper,fLower] The hardware upper and lower limits for the motor.
|
||||
These are the real hardware limits as implemented by limit switches bolted
|
||||
to the instrument.
|
||||
\item[name] The real motor name.
|
||||
\item[GetPosition] retrieves the current hardware position of the motor into
|
||||
fPos.
|
||||
\item[RunTo] starts the motor to run to fNewVal. This sends a command to the
|
||||
motor and returns immediately.
|
||||
\item[GetStatus] requests the status from the motor. Possible answers are:
|
||||
\begin{description}
|
||||
\item[HWIdle, OKOK] The motor has finished or is idle.
|
||||
\item[HWFault] A fault has been found at the motor.
|
||||
\item[HWBusy] The motor is busy moving.
|
||||
\item[HWWarn] The motor hardware complained but managed to reach the
|
||||
requested position.
|
||||
\item[HWPosFault] The motor could not go where it should go for some reason.
|
||||
However, the motor is intact and ready to move somewhere else. This can
|
||||
happen when there is a concrete block in the instruments way which is not
|
||||
accounted for by a limit switch. This can also happen when the motor hits a
|
||||
limit switch. However, the general idea of SICS is that the limits will be
|
||||
checked for before even starting to run the motor. In order for this to
|
||||
work, the limits SICS knows about must macth those actually implemented.
|
||||
\end{description}
|
||||
\item[GetError] This gets called when an error has been detected during one
|
||||
of the previous operations. The function has to return more information
|
||||
about the error: iCode an driver dependent integer error code and maximum
|
||||
iErrLen characters of problem description in error.
|
||||
\item[TryAndFixIt] takes the integer error code returned in iCode from
|
||||
GetError and tries to solve the problem with the hardware. This function can
|
||||
either return MOTREDO which means the problem has been fixed and the
|
||||
operation needs to be redone, MOTOK when the problem has been fixed and no
|
||||
resending of a command is necessary or MOTFAIL which means that it is not
|
||||
possible to resolve the problem in software.
|
||||
\item[ContinueAfterWarn] will be called after a warning from the motor. This
|
||||
function is supposed to do whatever is needed to clear the warning from the
|
||||
motor and keep him going.
|
||||
\item[Halt] emergency stop the motor, NOW.
|
||||
\end{description}
|
||||
|
||||
As an example for a derived motor driver the SINQ EL734 motor driver is
|
||||
shown below. It add to the general fields the special things for that motor:
|
||||
host, port and channel number of the motor controller and a pointer to the
|
||||
driver communications structure.
|
||||
\begin{verbatim}
|
||||
typedef struct __MoDriv {
|
||||
/* 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 (*ContinueAfterWarn)(void *self);
|
||||
int (*Halt)(void *self);
|
||||
|
||||
|
||||
/* EL-734 specific fields */
|
||||
int iPort;
|
||||
char *hostname;
|
||||
int iChannel;
|
||||
int iMotor;
|
||||
void *EL734struct;
|
||||
int iMSR;
|
||||
} EL734Driv;
|
||||
\end{verbatim}
|
||||
|
||||
Most of the interaction with the motor driver happens through the functions
|
||||
defined in its data structure. The rest is creation and deletion:
|
||||
\begin{description}
|
||||
\item[MotorDriver *CreateEL734(SConnection *pCon, int argc, char
|
||||
*argv[])]
|
||||
creates a EL734 motor driver. The argc, \verb+argv[]+ pair contains the
|
||||
necessary drive parameters.
|
||||
\item[MotorDriver *CreateEL734DC(SConnection *pCon, int argc, char
|
||||
*argv[])]
|
||||
created an EL734 DC motor driver.
|
||||
\item[void KillEL734(void *pData)] deletes a EL734 motor driver.
|
||||
\item[MotorDriver *CreateSIM(SConnection *pCon, int argc, char *argv[])]
|
||||
creates a simulation motor driver.
|
||||
\item[void KillSIM(void *pData)] deletes a simulated motor driver.
|
||||
\end{description}
|
||||
|
||||
\subsubsection{The Motor Logical Object}
|
||||
The motor object represents the motor to SICS. One of its responsabilities
|
||||
is to drive motor operations and error checking. The scheme
|
||||
implemented is that the motor object tries to bring the motor to its
|
||||
position at least three times before a failure is recorded. Also the
|
||||
motor object keeps track of a count of failed operations. If this
|
||||
count gets to high an interrupt is issued to stop the instrument. This
|
||||
was put in after Druechal tried to drive over a slab of concrete for a
|
||||
whole night and subsequently broke a clutch.
|
||||
Motors are represented by the
|
||||
following data structure:
|
||||
\begin{verbatim}
|
||||
typedef struct __Motor {
|
||||
pObjectDescriptor pDescriptor;
|
||||
ObPar *ParArray;
|
||||
pIDrivable pDrivInt;
|
||||
pICallBack pCall;
|
||||
char *drivername;
|
||||
char *name;
|
||||
MotorDriver *pDriver;
|
||||
float fTarget;
|
||||
float fPosition;
|
||||
} Motor;
|
||||
typedef Motor *pMotor;
|
||||
\end{verbatim}
|
||||
The fields: \begin{description}
|
||||
\item[pDescriptor] The usual SICS object descriptor.
|
||||
\item[ParArray] A dictionary of parameter names and float values. This array
|
||||
holds all the different parameters used to operate the motor.
|
||||
\item[pDrivInt] A pointer to the drivable interface implemented by the motor
|
||||
object.
|
||||
\item[pCall] A pointer to the callback interface implemented by the motor
|
||||
object.
|
||||
\item[drivername] The name of the motor driver.
|
||||
\item[name] The name of the motor.
|
||||
\item[pDriver] A pointer to the motor driver to use.
|
||||
\item[fTarget] The target position for the motor.
|
||||
\item[fPosition] The last known position of the motor.
|
||||
\end{description}
|
||||
|
||||
Much of the action of the motor is hidden in the implementation of the
|
||||
drivable interface to the motor. Additionally the functions as given below
|
||||
are defined. All functions take a pointer to the motor object data structure
|
||||
as a parameter. They retun 0 on success or 1 on failure while not stated
|
||||
otherwise.
|
||||
\begin{description}
|
||||
\item[int MotorGetPar(pMotor self, char *name, float *fVal)] retrieves the
|
||||
value of the parameter name in fVal.
|
||||
\item[int MotorSetPar(pMotor self, SConnection *pCon, char *name, float
|
||||
fVal)] tries to write fVal to the parameter name. Errors are written to
|
||||
the connection pCon.
|
||||
\item[long MotorRun(void *self, SConnection *pCon, float fNew)] starts a
|
||||
motor running to fNew. Does not wait for the motor to finish.
|
||||
\item[int MotorCheckBoundary(pMotor self, float fVal, float *fHard,
|
||||
char *error, int iErrLen)] checks if the position
|
||||
fVal violates any of the motors software or hardware limits. In case of
|
||||
success a new hardware position is returned in fHard. fHard is then
|
||||
corrected for possible software zero points. In case of a limit violation
|
||||
maximum iErrLen characters of error information are returned in error.
|
||||
\item[int MotorCheckPosition(void *self, SConnection *pCon)] returns 1 if
|
||||
the motor is at the target position, 0 else, or -1 if the motor could not be
|
||||
read.
|
||||
\item[int MotorGetSoftPosition(pMotor self,SConnection *pCon, float *fVal)]
|
||||
reads the current motor position into fVal. This position is corrected for
|
||||
software zero points.
|
||||
\item[int MotorGetHardPosition(pMotor self,SConnection *pCon, float *fVal)]
|
||||
reads the current position of the motor as returned from the hardware.
|
||||
\item[int MakeMotor(SConnection *pCon, SicsInterp *pSics, void *pData,
|
||||
int argc, char *argv[])] the motor factory function. To
|
||||
be hacked when a new driver needs to be included.
|
||||
\item[pMotor MotorInit(char *drivername,char *name, MotorDriver *pDriv)]
|
||||
creates a new motor object with driver pDriv. Returns NULL on failure.
|
||||
\item[void MotorKill(void *self)] deletes a motor object. Needs to be
|
||||
modified for a new driver in order to clean out the new driver properly.
|
||||
\item[int MotorAction(SConnection *pCon, SicsInterp *pSics, void *pData,
|
||||
int argc, char *argv[])] the object wrapper function for
|
||||
the motor. Implements the user interface.
|
||||
\item[pMotor FindMotor(SicsInterp *pSics, char *name)] finds a motor name in
|
||||
the interpreter pSics. This is a convenience function. Returns NULL if no
|
||||
motor could be found, else a pointer to its data structure.
|
||||
\end{description}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user