forked from epics_driver_modules/motorBase
681 lines
22 KiB
C
681 lines
22 KiB
C
/*
|
|
* devMotorAsyn.c
|
|
*
|
|
* Motor record common Asyn device support layer
|
|
*
|
|
* Copyright (C) 2005-6 Peter Denison, Diamond Light Source
|
|
*
|
|
* This software is distributed subject to the EPICS Open Licence, which can
|
|
* be found at http://www.aps.anl.gov/epics/licence/open.php
|
|
*
|
|
* Notwithstanding the above, explicit permission is granted for APS to
|
|
* redistribute this software.
|
|
*
|
|
* Version: $Revision: 1.32 $
|
|
* Modified by: $Author: rivers $
|
|
* Last Modified: $Date: 2009-09-01 14:05:38 $
|
|
*
|
|
* Original Author: Peter Denison
|
|
* Current Author: Peter Denison
|
|
*
|
|
* Modification Log:
|
|
* -----------------
|
|
* .01 2009-04-15 rls
|
|
* Added logic to asynCallback() to prevent moveRequestPending left nonzero
|
|
* after motor record LOAD_POS command before dbScanLockOK is true (i.e., from
|
|
* save/restore at boot-up).
|
|
* Eliminated compiler warnings.
|
|
*
|
|
* .02 2009-04-29 MRP
|
|
* Fix for motor simulator stuck in Moving state after multiple LOAD_POS
|
|
* commands to the same position; set needUpdate = 1 in asynCallback() before
|
|
* dbProcess.
|
|
*/
|
|
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
|
|
#include <dbAccess.h>
|
|
#include <recGbl.h>
|
|
#include <recSup.h>
|
|
#include <errlog.h>
|
|
#include <devSup.h>
|
|
#include <alarm.h>
|
|
#include <epicsEvent.h>
|
|
#include <cantProceed.h> /* !! for callocMustSucceed() */
|
|
|
|
#include <asynDriver.h>
|
|
#include <asynInt32.h>
|
|
#include <asynFloat64.h>
|
|
#include <asynDrvUser.h>
|
|
#include <asynFloat64Array.h>
|
|
#include <asynGenericPointer.h>
|
|
#include <asynEpicsUtils.h>
|
|
|
|
#include "motorRecord.h"
|
|
#include "motor.h"
|
|
#include "epicsExport.h"
|
|
#include "asynMotorDriver.h"
|
|
#include "motor_interface.h"
|
|
|
|
/*Create the dset for devMotor */
|
|
static long init( int after );
|
|
static long init_record(struct motorRecord *);
|
|
static CALLBACK_VALUE update_values(struct motorRecord *);
|
|
static long start_trans(struct motorRecord *);
|
|
static RTN_STATUS build_trans( motor_cmnd, double *, struct motorRecord *);
|
|
static RTN_STATUS end_trans(struct motorRecord *);
|
|
static void asynCallback(asynUser *);
|
|
static void statusCallback(void *, asynUser *, void *);
|
|
|
|
typedef enum {int32Type, float64Type, float64ArrayType} interfaceType;
|
|
|
|
struct motor_dset devMotorAsyn={ { 8,
|
|
NULL,
|
|
(DEVSUPFUN) init,
|
|
(DEVSUPFUN) init_record,
|
|
NULL },
|
|
update_values,
|
|
start_trans,
|
|
build_trans,
|
|
end_trans };
|
|
|
|
epicsExportAddress(dset,devMotorAsyn);
|
|
|
|
/* Note, we define these commands here. These are not pasynUser->reason, they are
|
|
* an index into those reasons returned from driver */
|
|
typedef enum motorCommand {
|
|
motorMoveAbs,
|
|
motorMoveRel,
|
|
motorMoveVel,
|
|
motorHome,
|
|
motorStop,
|
|
motorVelocity,
|
|
motorVelBase,
|
|
motorAccel,
|
|
motorPosition,
|
|
motorResolution,
|
|
motorEncRatio,
|
|
motorPgain,
|
|
motorIgain,
|
|
motorDgain,
|
|
motorHighLim,
|
|
motorLowLim,
|
|
motorSetClosedLoop,
|
|
motorStatus,
|
|
motorUpdateStatus,
|
|
lastMotorCommand
|
|
} motorCommand;
|
|
#define NUM_MOTOR_COMMANDS lastMotorCommand
|
|
|
|
typedef struct {
|
|
motorCommand command;
|
|
interfaceType interface;
|
|
int ivalue;
|
|
double dvalue;
|
|
} motorAsynMessage;
|
|
|
|
typedef struct
|
|
{
|
|
struct motorRecord * pmr;
|
|
int moveRequestPending;
|
|
struct MotorStatus status;
|
|
motorCommand move_cmd;
|
|
double param;
|
|
int needUpdate;
|
|
asynUser *pasynUser;
|
|
asynInt32 *pasynInt32;
|
|
void *asynInt32Pvt;
|
|
asynFloat64 *pasynFloat64;
|
|
void *asynFloat64Pvt;
|
|
asynFloat64Array *pasynFloat64Array;
|
|
void *asynFloat64ArrayPvt;
|
|
asynDrvUser *pasynDrvUser;
|
|
void *asynDrvUserPvt;
|
|
asynGenericPointer *pasynGenericPointer;
|
|
void *asynGenericPointerPvt;
|
|
void *registrarPvt;
|
|
epicsEventId initEvent;
|
|
int driverReasons[NUM_MOTOR_COMMANDS];
|
|
} motorAsynPvt;
|
|
|
|
|
|
|
|
/* The init routine is used to set a flag to indicate that it is OK to call dbScanLock */
|
|
static int dbScanLockOK = 0;
|
|
static long init( int after )
|
|
{
|
|
dbScanLockOK = (after!=0);
|
|
return 0;
|
|
}
|
|
|
|
static void init_controller(struct motorRecord *pmr, asynUser *pasynUser )
|
|
{
|
|
/* This routine is copied out of the old motordevCom and initialises the controller
|
|
based on the record values. I think most of it should be transferred to init_record
|
|
which is one reason why I have separated it into another routine */
|
|
motorAsynPvt *pPvt = (motorAsynPvt *)pmr->dpvt;
|
|
double position = pPvt->status.position;
|
|
double rdbd = (fabs(pmr->rdbd) < fabs(pmr->mres) ? fabs(pmr->mres) : fabs(pmr->rdbd) );
|
|
|
|
if ((fabs(pmr->dval) > rdbd && pmr->mres != 0) &&
|
|
(fabs(position * pmr->mres) < rdbd))
|
|
{
|
|
double setPos = pmr->dval / pmr->mres;
|
|
epicsEventId initEvent = epicsEventCreate( epicsEventEmpty );
|
|
|
|
pPvt->initEvent = initEvent;
|
|
|
|
start_trans(pmr);
|
|
build_trans(LOAD_POS, &setPos, pmr);
|
|
end_trans(pmr);
|
|
|
|
asynPrint(pasynUser, ASYN_TRACE_FLOW,
|
|
"devMotorAsyn::init_controller, %s set position to %f\n",
|
|
pmr->name, setPos );
|
|
|
|
if ( initEvent )
|
|
{
|
|
epicsEventMustWait(initEvent);
|
|
epicsEventDestroy(initEvent);
|
|
pPvt->initEvent = 0;
|
|
}
|
|
}
|
|
else
|
|
asynPrint(pasynUser, ASYN_TRACE_FLOW,
|
|
"devMotorAsyn::init_controller, %s setting of position not required, position=%f, mres=%f, dval=%f, rdbd=%f",
|
|
pmr->name, position, pmr->mres, pmr->dval, rdbd );
|
|
|
|
}
|
|
|
|
static long findDrvInfo(motorRecord *pmotor, asynUser *pasynUser, char *drvInfoString, int command)
|
|
{
|
|
motorAsynPvt *pPvt = (motorAsynPvt *)pmotor->dpvt;
|
|
|
|
/* Look up the pasynUser->reason */
|
|
if (pPvt->pasynDrvUser->create(pPvt->asynDrvUserPvt, pasynUser, drvInfoString, NULL, NULL) != asynSuccess) {
|
|
asynPrint(pasynUser, ASYN_TRACE_ERROR,
|
|
"devMotorAsyn::findDrvInfo, %s drvUserCreate failed for %s\n",
|
|
pmotor->name, drvInfoString);
|
|
return(-1);
|
|
}
|
|
pPvt->driverReasons[command] = pasynUser->reason;
|
|
return(0);
|
|
}
|
|
|
|
static long init_record(struct motorRecord * pmr )
|
|
{
|
|
asynUser *pasynUser;
|
|
char *port, *userParam;
|
|
int signal;
|
|
asynStatus status;
|
|
asynInterface *pasynInterface;
|
|
motorAsynPvt *pPvt;
|
|
/* double resolution;*/
|
|
|
|
/* Allocate motorAsynPvt private structure */
|
|
pPvt = callocMustSucceed(1, sizeof(motorAsynPvt), "devMotorAsyn init_record()");
|
|
|
|
/* Create asynUser */
|
|
pasynUser = pasynManager->createAsynUser(asynCallback, 0);
|
|
pasynUser->userPvt = pPvt;
|
|
pPvt->pasynUser = pasynUser;
|
|
pPvt->pmr = pmr;
|
|
pmr->dpvt = pPvt;
|
|
|
|
status = pasynEpicsUtils->parseLink(pasynUser, &pmr->out,
|
|
&port, &signal, &userParam);
|
|
if (status != asynSuccess) {
|
|
errlogPrintf("devMotorAsyn::init_record %s bad link %s\n",
|
|
pmr->name, pasynUser->errorMessage);
|
|
goto bad;
|
|
}
|
|
|
|
/* Connect to device */
|
|
status = pasynManager->connectDevice(pasynUser, port, signal);
|
|
if (status != asynSuccess) {
|
|
asynPrint(pasynUser, ASYN_TRACE_ERROR,
|
|
"devMotorAsyn::init_record, %s connectDevice failed to %s\n",
|
|
pmr->name, port);
|
|
goto bad;
|
|
}
|
|
|
|
/* Get the asynInt32 interface */
|
|
pasynInterface = pasynManager->findInterface(pasynUser, asynInt32Type, 1);
|
|
if (!pasynInterface) {
|
|
asynPrint(pasynUser, ASYN_TRACE_ERROR,
|
|
"devMotorAsyn::init_record, %s find int32 interface failed\n",
|
|
pmr->name);
|
|
goto bad;
|
|
}
|
|
pPvt->pasynInt32 = (asynInt32 *)pasynInterface->pinterface;
|
|
pPvt->asynInt32Pvt = pasynInterface->drvPvt;
|
|
|
|
/* Get the asynFloat64 interface */
|
|
pasynInterface = pasynManager->findInterface(pasynUser, asynFloat64Type, 1); if (!pasynInterface) {
|
|
asynPrint(pasynUser, ASYN_TRACE_ERROR,
|
|
"devMotorAsyn::init_record, %s find float64 interface failed\n",
|
|
pmr->name);
|
|
goto bad;
|
|
}
|
|
pPvt->pasynFloat64 = (asynFloat64 *)pasynInterface->pinterface;
|
|
pPvt->asynFloat64Pvt = pasynInterface->drvPvt;
|
|
|
|
/* Get the asynDrvUser interface */
|
|
pasynInterface = pasynManager->findInterface(pasynUser, asynDrvUserType, 1); if (!pasynInterface) {
|
|
asynPrint(pasynUser, ASYN_TRACE_ERROR,
|
|
"devMotorAsyn::init_record, %s find drvUser interface failed\n",
|
|
pmr->name);
|
|
goto bad;
|
|
}
|
|
pPvt->pasynDrvUser = (asynDrvUser *)pasynInterface->pinterface;
|
|
pPvt->asynDrvUserPvt = pasynInterface->drvPvt;
|
|
|
|
/* Now that we have the drvUser interface get pasynUser->reason for each command */
|
|
if (findDrvInfo(pmr, pasynUser, motorMoveRelString, motorMoveRel)) goto bad;
|
|
if (findDrvInfo(pmr, pasynUser, motorMoveAbsString, motorMoveAbs)) goto bad;
|
|
if (findDrvInfo(pmr, pasynUser, motorMoveVelString, motorMoveVel)) goto bad;
|
|
if (findDrvInfo(pmr, pasynUser, motorHomeString, motorHome)) goto bad;
|
|
if (findDrvInfo(pmr, pasynUser, motorStopString, motorStop)) goto bad;
|
|
if (findDrvInfo(pmr, pasynUser, motorVelocityString, motorVelocity)) goto bad;
|
|
if (findDrvInfo(pmr, pasynUser, motorVelBaseString, motorVelBase)) goto bad;
|
|
if (findDrvInfo(pmr, pasynUser, motorAccelString, motorAccel)) goto bad;
|
|
if (findDrvInfo(pmr, pasynUser, motorPositionString, motorPosition)) goto bad;
|
|
if (findDrvInfo(pmr, pasynUser, motorResolutionString, motorResolution)) goto bad;
|
|
if (findDrvInfo(pmr, pasynUser, motorEncRatioString, motorEncRatio)) goto bad;
|
|
if (findDrvInfo(pmr, pasynUser, motorPgainString, motorPgain)) goto bad;
|
|
if (findDrvInfo(pmr, pasynUser, motorIgainString, motorIgain)) goto bad;
|
|
if (findDrvInfo(pmr, pasynUser, motorDgainString, motorDgain)) goto bad;
|
|
if (findDrvInfo(pmr, pasynUser, motorHighLimString, motorHighLim)) goto bad;
|
|
if (findDrvInfo(pmr, pasynUser, motorLowLimString, motorLowLim)) goto bad;
|
|
if (findDrvInfo(pmr, pasynUser, motorSetClosedLoopString, motorSetClosedLoop)) goto bad;
|
|
if (findDrvInfo(pmr, pasynUser, motorStatusString, motorStatus)) goto bad;
|
|
if (findDrvInfo(pmr, pasynUser, motorUpdateStatusString, motorUpdateStatus)) goto bad;
|
|
|
|
/* Get the asynFloat64Array interface */
|
|
pasynInterface = pasynManager->findInterface(pasynUser,
|
|
asynFloat64ArrayType, 1);
|
|
if (!pasynInterface) {
|
|
asynPrint(pasynUser, ASYN_TRACE_ERROR,
|
|
"devMotorAsyn::init_record, %s find float64Array interface failed\n",
|
|
pmr->name);
|
|
goto bad;
|
|
}
|
|
pPvt->pasynFloat64Array = (asynFloat64Array *)pasynInterface->pinterface;
|
|
pPvt->asynFloat64ArrayPvt = pasynInterface->drvPvt;
|
|
|
|
/* Get the asynGenericPointer interface */
|
|
pasynInterface = pasynManager->findInterface(pasynUser,
|
|
asynGenericPointerType, 1);
|
|
if (!pasynInterface) {
|
|
asynPrint(pasynUser, ASYN_TRACE_ERROR,
|
|
"devMotorAsyn::init_record, %s find genericPointer interface failed\n",
|
|
pmr->name);
|
|
goto bad;
|
|
}
|
|
pPvt->pasynGenericPointer = (asynGenericPointer *)pasynInterface->pinterface;
|
|
pPvt->asynGenericPointerPvt = pasynInterface->drvPvt;
|
|
|
|
/* Now connect the callback, to the Generic Pointer interface, which passes MotorStatus structure */
|
|
pasynUser = pasynManager->duplicateAsynUser(pPvt->pasynUser, asynCallback, 0);
|
|
pasynUser->reason = pPvt->driverReasons[motorStatus];
|
|
status = pPvt->pasynGenericPointer->registerInterruptUser(pPvt->asynGenericPointerPvt,
|
|
pasynUser,
|
|
statusCallback,
|
|
pPvt,
|
|
&pPvt->registrarPvt);
|
|
if(status!=asynSuccess) {
|
|
printf("%s devMotorAsyn::init_record registerInterruptUser %s\n",
|
|
pmr->name, pasynUser->errorMessage);
|
|
}
|
|
|
|
/* Initiate calls to get the initial motor parameters
|
|
Have to do it the long-winded way, because before iocInit, none of the
|
|
locks or scan queues are initialised, so calls to scanOnce(),
|
|
dbScanLock() etc will fail. */
|
|
pasynUser = pasynManager->duplicateAsynUser(pPvt->pasynUser, asynCallback, 0);
|
|
|
|
/* Send the motor resolution to the driver. This should be done in the record
|
|
* in the future ? */
|
|
/* DON'T DO THIS FOR NOW. THE NUMBER CAN COME TOO LATE TO BE OF USE TO THE DRIVER
|
|
resolution = pmr->mres;
|
|
pasynUser->reason = pPvt->driverReasons[motorResolution];
|
|
pPvt->pasynFloat64->write(pPvt->asynFloat64Pvt, pasynUser,
|
|
resolution);
|
|
*/
|
|
pasynUser->reason = pPvt->driverReasons[motorStatus];
|
|
status = pPvt->pasynGenericPointer->read(pPvt->asynGenericPointerPvt, pasynUser,
|
|
(void *)&pPvt->status);
|
|
if (status != asynSuccess) {
|
|
asynPrint(pasynUser, ASYN_TRACE_ERROR,
|
|
"%s devMotorAsyn.c::init_record: pasynGenericPointer->read "
|
|
"returned %s", pmr->name, pasynUser->errorMessage);
|
|
}
|
|
|
|
/* We must get the first set of status values from the controller before
|
|
* the initial setting of position. Otherwise we won't be able to decide
|
|
* whether or not to write new position values to the controller.
|
|
*/
|
|
init_controller(pmr, pasynUser);
|
|
/* Do not need to manually retrieve the new status values, as if they are
|
|
* set, a callback will be generated
|
|
*/
|
|
|
|
/* Finally, indicate to the motor record that these values can be used. */
|
|
pasynManager->freeAsynUser(pasynUser);
|
|
pPvt->needUpdate = 1;
|
|
|
|
return(0);
|
|
bad:
|
|
pmr->pact=1;
|
|
return(0);
|
|
}
|
|
|
|
|
|
|
|
CALLBACK_VALUE update_values(struct motorRecord * pmr)
|
|
{
|
|
motorAsynPvt * pPvt = (motorAsynPvt *) pmr->dpvt;
|
|
CALLBACK_VALUE rc;
|
|
|
|
rc = NOTHING_DONE;
|
|
|
|
asynPrint(pPvt->pasynUser, ASYN_TRACEIO_DEVICE,
|
|
"%s devMotorAsyn::update_values, needUpdate=%d\n",
|
|
pmr->name, pPvt->needUpdate);
|
|
if ( pPvt->needUpdate ) {
|
|
pmr->rmp = (epicsInt32)floor(pPvt->status.position + 0.5);
|
|
pmr->rep = (epicsInt32)floor(pPvt->status.encoder_posn + 0.5);
|
|
/* pmr->rvel = (epicsInt32)round(pPvt->status.velocity); */
|
|
pmr->msta = pPvt->status.status;
|
|
rc = CALLBACK_DATA;
|
|
pPvt->needUpdate = 0;
|
|
}
|
|
return (rc);
|
|
}
|
|
|
|
static long start_trans(struct motorRecord * pmr )
|
|
{
|
|
return(OK);
|
|
}
|
|
|
|
static RTN_STATUS build_trans( motor_cmnd command,
|
|
double * param,
|
|
struct motorRecord * pmr )
|
|
{
|
|
RTN_STATUS rtnind = OK;
|
|
asynStatus status;
|
|
motorAsynPvt *pPvt = (motorAsynPvt *)pmr->dpvt;
|
|
asynUser *pasynUser = pPvt->pasynUser;
|
|
motorAsynMessage *pmsg;
|
|
int need_call=0;
|
|
|
|
asynPrint(pasynUser, ASYN_TRACE_FLOW,
|
|
"devMotorAsyn::send_msg: %s command=%d, pact=%d\n",
|
|
pmr->name, command, pmr->pact);
|
|
|
|
/* These primitives don't cause a call to the Asyn layer */
|
|
switch ( command ) {
|
|
case MOVE_ABS:
|
|
pPvt->move_cmd = motorMoveAbs;
|
|
pPvt->param = *param;
|
|
break;
|
|
case MOVE_REL:
|
|
pPvt->move_cmd = motorMoveRel;
|
|
pPvt->param = *param;
|
|
break;
|
|
case HOME_FOR:
|
|
pPvt->move_cmd = motorHome;
|
|
pPvt->param = 1;
|
|
break;
|
|
case HOME_REV:
|
|
pPvt->move_cmd = motorHome;
|
|
pPvt->param = 0;
|
|
break;
|
|
default:
|
|
need_call = 1;
|
|
}
|
|
|
|
/* Decide whether to quit here */
|
|
if (!need_call) {
|
|
return (OK);
|
|
}
|
|
|
|
/* If we are already in COMM_ALARM then this server is not reachable,
|
|
* return */
|
|
if ((pmr->nsta == COMM_ALARM) || (pmr->stat == COMM_ALARM))
|
|
return(ERROR);
|
|
|
|
/* Make a copy of asynUser. This is needed because we can have multiple
|
|
* requests queued. It will be freed in the callback */
|
|
pasynUser = pasynManager->duplicateAsynUser(pasynUser, asynCallback, 0);
|
|
pmsg = pasynManager->memMalloc(sizeof *pmsg);
|
|
pmsg->ivalue=0;
|
|
pmsg->dvalue=0.;
|
|
pmsg->interface = float64Type;
|
|
pasynUser->userData = pmsg;
|
|
|
|
switch (command) {
|
|
case LOAD_POS:
|
|
pmsg->command = motorPosition;
|
|
pmsg->dvalue = *param;
|
|
pPvt->moveRequestPending++;
|
|
break;
|
|
case SET_VEL_BASE:
|
|
pmsg->command = motorVelBase;
|
|
pmsg->dvalue = *param;
|
|
break;
|
|
case SET_VELOCITY:
|
|
pmsg->command = motorVelocity;
|
|
pmsg->dvalue = *param;
|
|
break;
|
|
case SET_ACCEL:
|
|
pmsg->command = motorAccel;
|
|
pmsg->dvalue = *param;
|
|
break;
|
|
case GO:
|
|
pmsg->command = pPvt->move_cmd;
|
|
pmsg->dvalue = pPvt->param;
|
|
pPvt->move_cmd = -1;
|
|
pPvt->moveRequestPending++;
|
|
/* Do we need to set needUpdate and schedule a process here? */
|
|
/* or can we always guarantee to get at least one callback? */
|
|
/* Do we really need the callback? I assume so */
|
|
break;
|
|
case SET_ENC_RATIO:
|
|
pmsg->command = motorEncRatio;
|
|
pmsg->dvalue = param[0]/param[1];
|
|
break;
|
|
case STOP_AXIS:
|
|
pmsg->command = motorStop;
|
|
pmsg->interface = int32Type;
|
|
break;
|
|
case JOG:
|
|
case JOG_VELOCITY:
|
|
pmsg->command = motorMoveVel;
|
|
pmsg->dvalue = *param;
|
|
break;
|
|
case SET_PGAIN:
|
|
pmsg->command = motorPgain;
|
|
pmsg->dvalue = *param;
|
|
break;
|
|
case SET_IGAIN:
|
|
pmsg->command = motorIgain;
|
|
pmsg->dvalue = *param;
|
|
break;
|
|
case SET_DGAIN:
|
|
pmsg->command = motorDgain;
|
|
pmsg->dvalue = *param;
|
|
break;
|
|
case ENABLE_TORQUE:
|
|
pmsg->command = motorSetClosedLoop;
|
|
pmsg->ivalue = 1;
|
|
pmsg->interface = int32Type;
|
|
break;
|
|
case DISABL_TORQUE:
|
|
pmsg->command = motorSetClosedLoop;
|
|
pmsg->ivalue = 0;
|
|
pmsg->interface = int32Type;
|
|
break;
|
|
case PRIMITIVE:
|
|
asynPrint(pasynUser, ASYN_TRACE_ERROR,
|
|
"devMotorAsyn::send_msg: %s: PRIMITIVE no longer supported\n",
|
|
pmr->name);
|
|
return(ERROR);
|
|
case SET_HIGH_LIMIT:
|
|
pmsg->command = motorHighLim;
|
|
pmsg->dvalue = *param;
|
|
break;
|
|
case SET_LOW_LIMIT:
|
|
pmsg->command = motorLowLim;
|
|
pmsg->dvalue = *param;
|
|
break;
|
|
case GET_INFO:
|
|
pmsg->command = motorUpdateStatus;
|
|
pmsg->interface = int32Type;
|
|
break;
|
|
case SET_RESOLUTION:
|
|
pmsg->command = motorResolution;
|
|
pmsg->dvalue = *param;
|
|
break;
|
|
default:
|
|
asynPrint(pasynUser, ASYN_TRACE_ERROR,
|
|
"devMotorAsyn::send_msg: %s: motor command %d not recognised\n",
|
|
pmr->name, command);
|
|
return(ERROR);
|
|
}
|
|
|
|
/* Queue asyn request, so we get a callback when driver is ready */
|
|
pasynUser->reason = pPvt->driverReasons[pmsg->command];
|
|
status = pasynManager->queueRequest(pasynUser, 0, 0);
|
|
if (status != asynSuccess) {
|
|
asynPrint(pasynUser, ASYN_TRACE_ERROR,
|
|
"devMotorAsyn::send_msg: %s error calling queueRequest, %s\n",
|
|
pmr->name, pasynUser->errorMessage);
|
|
rtnind = ERROR;
|
|
}
|
|
return(rtnind);
|
|
}
|
|
|
|
static RTN_STATUS end_trans(struct motorRecord * pmr )
|
|
{
|
|
return(OK);
|
|
}
|
|
|
|
/**
|
|
* Called once the request comes off the Asyn internal queue.
|
|
*
|
|
* The request is still "on its way down" at this point
|
|
*/
|
|
static void asynCallback(asynUser *pasynUser)
|
|
{
|
|
motorAsynPvt *pPvt = (motorAsynPvt *)pasynUser->userPvt;
|
|
motorRecord *pmr = pPvt->pmr;
|
|
motorAsynMessage *pmsg = pasynUser->userData;
|
|
int status;
|
|
int commandIsMove = 0;
|
|
|
|
asynPrint(pasynUser, ASYN_TRACE_FLOW,
|
|
"devMotorAsyn::asynCallback: %s command=%d, ivalue=%d, dvalue=%f\n",
|
|
pmr->name, pmsg->command, pmsg->ivalue, pmsg->dvalue);
|
|
pasynUser->reason = pPvt->driverReasons[pmsg->command];
|
|
|
|
switch (pmsg->command) {
|
|
case motorStatus:
|
|
/* Read the current status of the device */
|
|
status = pPvt->pasynGenericPointer->read(pPvt->asynGenericPointerPvt,
|
|
pasynUser,
|
|
(void *)&pPvt->status);
|
|
if (status != asynSuccess) {
|
|
asynPrint(pasynUser, ASYN_TRACE_ERROR,
|
|
"devMotorAsyn::asynCallback: %s pasynGenericPointer->read"
|
|
"returned %s\n", pmr->name, pasynUser->errorMessage);
|
|
}
|
|
break;
|
|
|
|
case motorUpdateStatus:
|
|
status = pPvt->pasynInt32->write(pPvt->asynInt32Pvt, pasynUser,
|
|
pmsg->ivalue);
|
|
break;
|
|
|
|
case motorMoveAbs:
|
|
case motorMoveRel:
|
|
case motorHome:
|
|
case motorPosition:
|
|
commandIsMove = 1;
|
|
/* Intentional fall-through */
|
|
default:
|
|
if (pmsg->interface == int32Type) {
|
|
status = pPvt->pasynInt32->write(pPvt->asynInt32Pvt, pasynUser,
|
|
pmsg->ivalue);
|
|
} else {
|
|
status = pPvt->pasynFloat64->write(pPvt->asynFloat64Pvt, pasynUser,
|
|
pmsg->dvalue);
|
|
}
|
|
if (status != asynSuccess) {
|
|
asynPrint(pasynUser, ASYN_TRACE_ERROR,
|
|
"devMotorAsyn::asynCallback: %s pasyn{Float64,Int32}->"
|
|
"write returned %s\n", pmr->name, pasynUser->errorMessage);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (dbScanLockOK) { /* effectively if iocInit has completed */
|
|
dbScanLock((dbCommon *)pmr);
|
|
if (commandIsMove) {
|
|
pPvt->moveRequestPending--;
|
|
if (!pPvt->moveRequestPending) {
|
|
pPvt->needUpdate = 1;
|
|
/* pmr->rset->process((dbCommon*)pmr); */
|
|
dbProcess((dbCommon*)pmr);
|
|
}
|
|
}
|
|
dbScanUnlock((dbCommon *)pmr);
|
|
}
|
|
else if (pmsg->command == motorPosition)
|
|
pPvt->moveRequestPending = 0;
|
|
|
|
pasynManager->memFree(pmsg, sizeof(*pmsg));
|
|
status = pasynManager->freeAsynUser(pasynUser);
|
|
if (status != asynSuccess) {
|
|
asynPrint(pasynUser, ASYN_TRACE_ERROR,
|
|
"devMotorAsyn::asynCallback: %s error in freeAsynUser, %s\n",
|
|
pmr->name, pasynUser->errorMessage);
|
|
}
|
|
|
|
if ( pPvt->initEvent ) epicsEventSignal( pPvt->initEvent );
|
|
}
|
|
|
|
/**
|
|
* True callback to notify that controller status has changed.
|
|
*/
|
|
static void statusCallback(void *drvPvt, asynUser *pasynUser,
|
|
void *pValue)
|
|
{
|
|
motorAsynPvt *pPvt = (motorAsynPvt *)drvPvt;
|
|
motorRecord *pmr = pPvt->pmr;
|
|
MotorStatus *value = (MotorStatus *)pValue;
|
|
|
|
asynPrint(pasynUser, ASYN_TRACEIO_DEVICE,
|
|
"%s devMotorAsyn::statusCallback new value=[p:%f,e:%f,s:%x] %c%c\n",
|
|
pmr->name, value->position, value->encoder_posn, value->status,
|
|
pPvt->needUpdate?'N':' ', pPvt->moveRequestPending?'P':' ');
|
|
|
|
if (dbScanLockOK) {
|
|
dbScanLock((dbCommon *)pmr);
|
|
memcpy(&pPvt->status, value, sizeof(struct MotorStatus));
|
|
if (!pPvt->moveRequestPending) {
|
|
pPvt->needUpdate = 1;
|
|
/* pmr->rset->process((dbCommon*)pmr); */
|
|
dbProcess((dbCommon*)pmr);
|
|
}
|
|
dbScanUnlock((dbCommon*)pmr);
|
|
} else {
|
|
memcpy(&pPvt->status, value, sizeof(struct MotorStatus));
|
|
pPvt->needUpdate = 1;
|
|
}
|
|
}
|