From 9cc57ec1c7b381d28c5ae197136391a87cadb810 Mon Sep 17 00:00:00 2001 From: MarkRivers Date: Thu, 7 Apr 2011 03:35:42 +0000 Subject: [PATCH] Many changes to implement profile moves and readbacks --- motorApp/NewportSrc/XPSController.cpp | 469 ++++++++++++++++++++++---- 1 file changed, 397 insertions(+), 72 deletions(-) diff --git a/motorApp/NewportSrc/XPSController.cpp b/motorApp/NewportSrc/XPSController.cpp index 160677f4..ea5d367a 100644 --- a/motorApp/NewportSrc/XPSController.cpp +++ b/motorApp/NewportSrc/XPSController.cpp @@ -96,6 +96,8 @@ Versions: Release 4-5 and higher. static const char *driverName = "XPSController"; +static void XPSProfileThreadC(void *pPvt); + /** Struct for a list of strings describing the different corrector types possible on the XPS.*/ typedef struct { char *PIPosition; @@ -115,16 +117,16 @@ const static CorrectorTypes_t CorrectorTypes = { /* Constants used for FTP to the XPS */ #define TRAJECTORY_DIRECTORY "/Admin/public/Trajectories" -#define MAX_FILENAME_LEN 256 -#define MAX_MESSAGE_LEN 256 -#define MAX_GROUPNAME_LEN 64 +#define MAX_FILENAME_LEN 256 +#define MAX_MESSAGE_LEN 256 +#define MAX_GROUPNAME_LEN 64 /* The maximum size of the item names in gathering, e.g. "GROUP2.POSITIONER1.CurrentPosition" */ #define MAX_GATHERING_AXIS_STRING 60 /* Number of items per axis */ #define NUM_GATHERING_ITEMS 2 /* Total length of gathering configuration string */ -#define MAX_GATHERING_STRING MAX_GATHERING_AXIS_STRING * NUM_GATHERING_ITEMS * MAX_AXES +#define MAX_GATHERING_STRING MAX_GATHERING_AXIS_STRING * NUM_GATHERING_ITEMS * XPS_MAX_AXES // Maximum number of bytes that GatheringDataMultipleLinesGet() can return #define GATHERING_MAX_READ_LEN 65536 @@ -136,8 +138,8 @@ XPSController::XPSController(const char *portName, const char *IPAddress, int IP int numAxes, double movingPollPeriod, double idlePollPeriod, int enableSetPosition, double setPositionSettlingTime) : asynMotorController(portName, numAxes, NUM_XPS_PARAMS, - asynInt32Mask | asynFloat64Mask | asynUInt32DigitalMask, - asynInt32Mask | asynFloat64Mask | asynUInt32DigitalMask, + 0, // No additional interfaces + 0, // No addition interrupt interfaces ASYN_CANBLOCK | ASYN_MULTIDEVICE, 1, // autoconnect 0, 0), // Default priority and stack size @@ -162,7 +164,7 @@ XPSController::XPSController(const char *portName, const char *IPAddress, int IP createParam(XPSStatusString, asynParamInt32, &XPSStatus_); // This socket is used for polling by the controller and all axes - pollSocket_ = TCP_ConnectToServer((char *)IPAddress, IPPort, TCP_TIMEOUT); + pollSocket_ = TCP_ConnectToServer((char *)IPAddress, IPPort, XPS_POLL_TIMEOUT); if (pollSocket_ < 0) { printf("%s:%s: error calling TCP_ConnectToServer for pollSocket\n", driverName, functionName); @@ -170,7 +172,7 @@ XPSController::XPSController(const char *portName, const char *IPAddress, int IP // This socket is used for moving motors during profile moves // Each axis also has its own moveSocket - moveSocket_ = TCP_ConnectToServer((char *)IPAddress, IPPort, TCP_TIMEOUT); + moveSocket_ = TCP_ConnectToServer((char *)IPAddress, IPPort, XPS_MOVE_TIMEOUT); if (moveSocket_ < 0) { printf("%s:%s: error calling TCP_ConnectToServer for moveSocket\n", driverName, functionName); @@ -181,6 +183,16 @@ XPSController::XPSController(const char *portName, const char *IPAddress, int IP /* Create the poller thread for this controller * NOTE: at this point the axis objects don't yet exist, but the poller tolerates this */ startPoller(movingPollPeriod, idlePollPeriod, 10); + + // Create the event that wakes up the thread for profile moves + profileExecuteEvent_ = epicsEventMustCreate(epicsEventEmpty); + + // Create the thread that will execute profile moves + epicsThreadCreate("XPSProfile", + epicsThreadPriorityLow, + epicsThreadGetStackSize(epicsThreadStackMedium), + (EPICSTHREADFUNC)XPSProfileThreadC, (void *)this); + } void XPSController::report(FILE *fp, int level) @@ -344,10 +356,13 @@ asynStatus XPSController::writeFloat64(asynUser *pasynUser, epicsFloat64 value) { int function = pasynUser->reason; int status = asynSuccess; - XPSAxis *pAxis = this->getAxis(pasynUser); + XPSAxis *pAxis; double deviceValue; static const char *functionName = "writeFloat64"; + pAxis = this->getAxis(pasynUser); + if (!pAxis) return asynError; + /* Set the parameter and readback in the parameter library. */ status = pAxis->setDoubleParam(function, value); @@ -434,9 +449,12 @@ asynStatus XPSController::writeInt32(asynUser *pasynUser, epicsInt32 value) { int function = pasynUser->reason; int status = asynSuccess; - XPSAxis *pAxis = this->getAxis(pasynUser); + XPSAxis *pAxis; static const char *functionName = "writeInt32"; + pAxis = this->getAxis(pasynUser); + if (!pAxis) return asynError; + /* Set the parameter and readback in the parameter library. This may be overwritten when we read back the * status at the end, but that's OK */ status = pAxis->setIntegerParam(function, value); @@ -513,6 +531,22 @@ XPSAxis* XPSController::getAxis(int axisNo) } +asynStatus XPSController::waitMotors() +{ + bool moving, anyMoving=true; + int j; + + while (anyMoving) { + anyMoving = false; + for (j=0; jpoll(&moving); + if (moving) anyMoving = true; + } + epicsThreadSleep(0.1); + } + return asynSuccess; +} + /* Function to initialize profile */ asynStatus XPSController::initializeProfile(size_t maxPoints, const char* ftpUsername, const char* ftpPassword) @@ -531,7 +565,7 @@ asynStatus XPSController::buildProfile() FILE *trajFile; int i, j; int status; - bool buildOK=false; + bool buildOK=true; bool verifyOK=true; int nPoints; int nElements; @@ -540,7 +574,7 @@ asynStatus XPSController::buildProfile() int ftpSocket; char fileName[MAX_FILENAME_LEN]; char groupName[MAX_GROUPNAME_LEN]; - char buildMessage[MAX_MESSAGE_LEN]; + char message[MAX_MESSAGE_LEN]; int buildStatus; double distance; double maxVelocity; @@ -565,9 +599,10 @@ asynStatus XPSController::buildProfile() // Call the base class method which will build the time array if needed asynMotorController::buildProfile(); + strcpy(message, ""); + setStringParam(profileBuildMessage_, message); setIntegerParam(profileBuildState_, PROFILE_BUILD_BUSY); setIntegerParam(profileBuildStatus_, PROFILE_STATUS_UNDEFINED); - strcpy(buildMessage, ""); callParamCallbacks(); /* We create trajectories with an extra element at the beginning and at the end. @@ -599,7 +634,8 @@ asynStatus XPSController::buildProfile() &maxVelocity, &maxAcceleration, &minJerkTime, &maxJerkTime); if (status) { - sprintf(buildMessage, "Error calling positionerSGammaParametersSet, status=%d\n", status); + buildOK = false; + sprintf(message, "Error calling positionerSGammaParametersSet, status=%d\n", status); goto done; } @@ -627,6 +663,9 @@ asynStatus XPSController::buildProfile() for (j=0; jprofilePreDistance_ = preDistance[j]; + pAxes_[j]->profilePostDistance_ = postDistance[j]; } /* Create the profile file */ @@ -680,25 +719,28 @@ asynStatus XPSController::buildProfile() /* FTP the trajectory file from the local directory to the XPS */ status = ftpConnect(IPAddress_, ftpUsername_, ftpPassword_, &ftpSocket); if (status) { - sprintf(buildMessage, "Error calling ftpConnect, status=%d\n", status); + buildOK = false; + sprintf(message, "Error calling ftpConnect, status=%d\n", status); goto done; } status = ftpChangeDir(ftpSocket, TRAJECTORY_DIRECTORY); if (status) { - sprintf(buildMessage, "Error calling ftpChangeDir, status=%d\n", status); + buildOK = false; + sprintf(message, "Error calling ftpChangeDir, status=%d\n", status); goto done; } status = ftpStoreFile(ftpSocket, fileName); if (status) { - sprintf(buildMessage, "Error calling ftpStoreFile, status=%d\n", status); + buildOK = false; + sprintf(message, "Error calling ftpStoreFile, status=%d\n", status); goto done; } status = ftpDisconnect(ftpSocket); if (status) { - sprintf(buildMessage, "Error calling ftpDisconnect, status=%d\n", status); + buildOK = false; + sprintf(message, "Error calling ftpDisconnect, status=%d\n", status); goto done; } - buildOK = true; /* Verify trajectory */ asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, @@ -708,22 +750,22 @@ asynStatus XPSController::buildProfile() if (status) verifyOK = false; switch (-status) { case 0: - strcpy(buildMessage, " "); + strcpy(message, " "); break; case 69: - strcpy(buildMessage, "Acceleration Too High"); + strcpy(message, "Acceleration Too High"); break; case 68: - strcpy(buildMessage, "Velocity Too High"); + strcpy(message, "Velocity Too High"); break; case 70: - strcpy(buildMessage, "Final Velocity Non Zero"); + strcpy(message, "Final Velocity Non Zero"); break; case 75: - strcpy(buildMessage, "Negative or Null Delta Time"); + strcpy(message, "Negative or Null Delta Time"); break; default: - sprintf(buildMessage, "Unknown trajectory verify error=%d", status); + sprintf(message, "Unknown trajectory verify error=%d", status); break; } @@ -740,9 +782,9 @@ asynStatus XPSController::buildProfile() pAxes_[j]->setDoubleParam(XPSProfileMaxVelocity_, maxVelocityActual); pAxes_[j]->setDoubleParam(XPSProfileMaxAcceleration_, maxAccelerationActual); if (status) { - sprintf(buildMessage, "MultipleAxesPVTVerificationResultGet error for axis %s, status=%d\n", - pAxes_[j]->positionerName_, status); verifyOK = false; + sprintf(message, "MultipleAxesPVTVerificationResultGet error for axis %s, status=%d\n", + pAxes_[j]->positionerName_, status); } /* Check that the trajectory won't exceed the software limits * The XPS does not check this because the trajectory is defined in relative moves and it does @@ -753,27 +795,25 @@ asynStatus XPSController::buildProfile() &highLimit); minProfile = pAxes_[j]->profilePositions_[0] + minPositionActual; if (minProfile < lowLimit) { - status = 1; - sprintf(buildMessage, "Low soft limit violation for axis %s, position=%f, limit=%f\n", - pAxes_[j]->positionerName_, minProfile, lowLimit); verifyOK = false; + sprintf(message, "Low soft limit violation for axis %s, position=%f, limit=%f\n", + pAxes_[j]->positionerName_, minProfile, lowLimit); } maxProfile = pAxes_[j]->profilePositions_[0] + maxPositionActual; if (maxProfile > highLimit) { - status = 1; - sprintf(buildMessage, "High soft limit violation for axis %s, position=%f, limit=%f\n", - pAxes_[j]->positionerName_, maxProfile, highLimit); verifyOK = false; + sprintf(message, "High soft limit violation for axis %s, position=%f, limit=%f\n", + pAxes_[j]->positionerName_, maxProfile, highLimit); } } done: buildStatus = (buildOK && verifyOK) ? PROFILE_STATUS_SUCCESS : PROFILE_STATUS_FAILURE; setIntegerParam(profileBuildStatus_, buildStatus); - setStringParam(profileBuildMessage_, buildMessage); - if (status) { + setStringParam(profileBuildMessage_, message); + if (buildStatus != PROFILE_STATUS_SUCCESS) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: %s\n", - driverName, functionName, buildMessage); + driverName, functionName, message); } /* Clear build command. This is a "busy" record, don't want to do this until build is complete. */ setIntegerParam(profileBuild_, 0); @@ -785,13 +825,306 @@ asynStatus XPSController::buildProfile() /* Function to execute trajectory */ asynStatus XPSController::executeProfile() { + epicsEventSignal(profileExecuteEvent_); return asynSuccess; } +/* C Function which runs the profile thread */ +static void XPSProfileThreadC(void *pPvt) +{ + XPSController *pC = (XPSController*)pPvt; + pC->profileThread(); +} + + +/* Function which runs in its own thread to execute profiles */ +void XPSController::profileThread() +{ + while (true) { + epicsEventWait(profileExecuteEvent_); + runProfile(); + } +} + +/* Function to run trajectory. It runs in a dedicated thread, so it's OK to block. + * It needs to lock and unlock when it accesses class data. */ +asynStatus XPSController::runProfile() +{ + int status; + bool executeOK=true; + bool aborted=false; + int j; + int startPulses, endPulses; + int numPoints, numPulses; + int executeStatus; + double pulsePeriod; + double position; + double time; + int i; + char message[MAX_MESSAGE_LEN]; + char buffer[MAX_GATHERING_STRING]; + char fileName[MAX_FILENAME_LEN]; + char groupName[MAX_GROUPNAME_LEN]; + int eventId; + int useAxis[XPS_MAX_AXES]; + XPSAxis *pAxis; + static const char *functionName = "runProfile"; + + lock(); + getStringParam(XPSTrajectoryFile_, (int)sizeof(fileName), fileName); + getStringParam(XPSProfileGroupName_, (int)sizeof(groupName), groupName); + getIntegerParam(profileStartPulses_, &startPulses); + getIntegerParam(profileEndPulses_, &endPulses); + getIntegerParam(profileNumPoints_, &numPoints); + getIntegerParam(profileNumPulses_, &numPulses); + for (j=0; jprofilePositions_[numPoints-1] - pAxis->profilePreDistance_; + status = GroupMoveAbsolute(pAxis->moveSocket_, + pAxis->positionerName_, + 1, + &position); + } + + // Wait for the motors to get there + wakeupPoller(); + waitMotors(); + + lock(); + setIntegerParam(profileExecuteState_, PROFILE_EXECUTE_EXECUTING); + callParamCallbacks(); + unlock(); + + /* Configure Gathering */ + /* Reset gathering. + * This must be done because GatheringOneData just appends to in-memory list */ + status = GatheringReset(pollSocket_); + if (status != 0) { + executeOK = false; + sprintf(message, "Error performing GatheringReset, status=%d",status); + goto done; + } + + /* Write list of gathering parameters. + * Note that there must be NUM_GATHERING_ITEMS per axis in this list. */ + strcpy(buffer, ""); + for (j=0; jpositionerName_); + strcat (buffer, ".SetpointPosition;"); + strcat (buffer, pAxes_[j]->positionerName_); + strcat (buffer, ".CurrentPosition;"); + } + + /* Define what is to be saved in the GatheringExternal.dat. + * 3 pieces of information per axis. */ + status = GatheringConfigurationSet(pollSocket_, + numAxes_*NUM_GATHERING_ITEMS, buffer); + if (status != 0) { + executeOK = false; + sprintf(message, "Error performing GatheringConfigurationSet, status=%d, buffer=%s", + status, buffer); + goto done; + } + + // Check valid range of start and end pulses; these start at 1, not 0 + if ((startPulses < 1) || (startPulses > numPoints) || + (endPulses < startPulses) || (endPulses > numPoints)) { + executeOK = false; + sprintf(message, "Error: start or end pulses outside valid range"); + goto done; + } + // The XPS can only output pulses at a fixed period, not a fixed distance along the trajectory. + // The trajectory elements where pulses start and stop are defined by startPulses and endPulses. + // Compute the time between pulses as the total time over which pulses should be output divided + // by the number of pulses to be output. */ + time = 0; + for (i=startPulses; i<=endPulses; i++) { + time += profileTimes_[i-1]; + } + if (numPulses != 0) + pulsePeriod = time / numPulses; + else + pulsePeriod = 0; + + /* Define trajectory output pulses. + * startPulses and endPulses are defined as 1=first real element, need to add + * 1 to each to skip the acceleration element. + * The XPS is told the element to stop outputting pulses, and it seems to stop + * outputting at the start of that element. So we need to have that element be + * the decceleration endPulses is the element, which means adding another +1. */ + status = MultipleAxesPVTPulseOutputSet(pollSocket_, groupName, + startPulses+1, + endPulses+1, + pulsePeriod); + + /* Define trigger */ + sprintf(buffer, "Always;%s.PVT.TrajectoryPulse", groupName); + status = EventExtendedConfigurationTriggerSet(pollSocket_, 2, buffer, + "", "", "", ""); + if (status != 0) { + executeOK = false; + sprintf(message, "Error performing EventExtendedConfigurationTriggerSet, status=%d, buffer=%s", + status, buffer); + goto done; + } + + /* Define action */ + status = EventExtendedConfigurationActionSet(pollSocket_, 1, + "GatheringOneData", + "", "", "", ""); + if (status != 0) { + executeOK = false; + sprintf(message, "Error performing EventExtendedConfigurationActionSet, status=%d", + status); + goto done; + } + + /* Start gathering */ + status= EventExtendedStart(pollSocket_, &eventId); + if (status != 0) { + executeOK = false; + sprintf(message, "Error performing EventExtendedStart, status=%d", + status); + goto done; + } + + wakeupPoller(); + + /* We call the command to run the trajectory on the moveSocket which does not + * wait for a reply. Thus this routine returns immediately without a meaningful + * status */ + status = MultipleAxesPVTExecution(moveSocket_, groupName, + fileName, 1); + /* status -27 means the trajectory was aborted */ + if (status == -27) { + executeOK = false; + aborted = true; + sprintf(message, "MultipleAxesPVTExecution aborted"); + } + else if (status != 0) { + executeOK = false; + sprintf(message, "Error performing MultipleAxesPVTExecution, status=%d", + status); + } + + /* Remove the event */ + status = EventExtendedRemove(pollSocket_, eventId); + if (status != 0) { + executeOK = false; + sprintf(message, "Error performing ExtendedEventRemove, status=%d", + status); + } + + /* Stop the gathering */ + status = GatheringStop(pollSocket_); + /* status -30 means gathering not started i.e. aborted before the end of + 1 trajectory element */ + if ((status != 0) && (status != -30)) { + executeOK = false; + sprintf(message, "Error performing GatheringStop, status=%d", + status); + } + + done: + lock(); + setIntegerParam(profileExecuteState_, PROFILE_EXECUTE_FLYBACK); + callParamCallbacks(); + unlock(); + + // Move the motors to the end position + for (j=0; jprofilePositions_[numPoints-1] + pAxis->profilePostDistance_; + status = GroupMoveAbsolute(pAxis->moveSocket_, + pAxis->positionerName_, + 1, + &position); + } + + // Wait for the motors to get there + wakeupPoller(); + waitMotors(); + + lock(); + if (executeOK) executeStatus = PROFILE_STATUS_SUCCESS; + else if (aborted) executeStatus = PROFILE_STATUS_ABORT; + else executeStatus = PROFILE_STATUS_FAILURE; + setIntegerParam(profileExecuteStatus_, executeStatus); + setStringParam(profileExecuteMessage_, message); + if (!executeOK) { + asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, + "%s:%s: %s\n", + driverName, functionName, message); + } + /* Clear execute command. This is a "busy" record, don't want to do this until build is complete. */ + setIntegerParam(profileExecute_, 0); + setIntegerParam(profileExecuteState_, PROFILE_EXECUTE_DONE); + callParamCallbacks(); + unlock(); + return executeOK ? asynSuccess : asynError; +} + +/** Polls the controller, rather than individual axis + * Used during profile moves */ +asynStatus XPSController::poll() +{ + int executeState; + int status; + int number; + char fileName[MAX_FILENAME_LEN]; + char groupName[MAX_GROUPNAME_LEN]; + + getIntegerParam(profileExecuteState_, &executeState); + if (executeState != PROFILE_EXECUTE_EXECUTING) return asynSuccess; + + getStringParam(XPSTrajectoryFile_, (int)sizeof(fileName), fileName); + getStringParam(XPSProfileGroupName_, (int)sizeof(groupName), groupName); + status = MultipleAxesPVTParametersGet(pollSocket_, groupName, fileName, &number); + if (status) return asynError; + setIntegerParam(profileCurrentPoint_, number); + callParamCallbacks(); + return asynSuccess; +} + + + +asynStatus XPSController::abortProfile() +{ + int status; + char groupName[MAX_GROUPNAME_LEN]; + static const char *functionName = "abortProfile"; + + getStringParam(XPSProfileGroupName_, (int)sizeof(groupName), groupName); + status = GroupMoveAbort(pollSocket_, groupName); + if (status != 0) { + asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, + "%s:%s: Error performing GroupMoveAbort, status=%d\n", + driverName, functionName, status); + return asynError; + } + return asynSuccess; +} + + + /* Function to readback trajectory */ asynStatus XPSController::readbackProfile() { - char readbackMessage[MAX_MESSAGE_LEN]; + char message[MAX_MESSAGE_LEN]; + bool readbackOK=true; int numPulses; char* buffer=NULL; char* bptr, *tptr; @@ -809,28 +1142,34 @@ asynStatus XPSController::readbackProfile() "%s:%s: entry\n", driverName, functionName); - setIntegerParam(profileBuildState_, PROFILE_BUILD_BUSY); - setIntegerParam(profileBuildStatus_, PROFILE_STATUS_UNDEFINED); - strcpy(readbackMessage, ""); + for (j=0; jprofileReadbacks_, 0, maxProfilePoints_*sizeof(double)); memset(pAxes_[j]->profileFollowingErrors_, 0, maxProfilePoints_*sizeof(double)); - getIntegerParam(j, profileUseAxis_, &useAxis[j]); } /* Read the number of lines of gathering */ status = GatheringCurrentNumberGet(pollSocket_, ¤tSamples, &maxSamples); if (status != 0) { - sprintf(readbackMessage, "Error calling GatherCurrentNumberGet, status=%d\n", status); + readbackOK = false; + sprintf(message, "Error calling GatherCurrentNumberGet, status=%d", status); goto done; } if (currentSamples != numPulses) { - sprintf(readbackMessage, "Error, numPulses=%d, currentSamples=%d\n", numPulses, currentSamples); - goto done; + readbackOK = false; + sprintf(message, "Error, numPulses=%d, currentSamples=%d", numPulses, currentSamples); + //goto done; } buffer = (char *)calloc(GATHERING_MAX_READ_LEN, sizeof(char)); numInBuffer = 0; @@ -847,7 +1186,8 @@ asynStatus XPSController::readbackProfile() if (status) numInBuffer /= 2; } if (numInBuffer == 0) { - sprintf(readbackMessage, "Error reading gathering data, numInBuffer = 0\n"); + readbackOK = false; + sprintf(message, "Error reading gathering data, numInBuffer = 0"); goto done; } bptr = buffer; @@ -861,7 +1201,8 @@ asynStatus XPSController::readbackProfile() &setpointPosition, &actualPosition, &numChars); bptr += numChars+1; if (nitems != NUM_GATHERING_ITEMS) { - sprintf(readbackMessage, "Error reading Gathering.dat file, nitems=%d, should be %d\n", + readbackOK = false; + sprintf(message, "Error reading Gathering.dat file, nitems=%d, should be %d", nitems, NUM_GATHERING_ITEMS); goto done; } @@ -876,43 +1217,27 @@ asynStatus XPSController::readbackProfile() done: if (buffer) free(buffer); - /* Call the base class method that converts from controller to user units and posts the arrays */ - asynMotorController::readbackProfile(); - readbackStatus = status ? PROFILE_STATUS_FAILURE : PROFILE_STATUS_SUCCESS; - setIntegerParam(profileReadbackStatus_, readbackStatus); - setStringParam(profileReadbackMessage_, readbackMessage); setIntegerParam(profileActualPulses_, numRead); setIntegerParam(profileNumReadbacks_, numRead); - if (status) { + /* Convert from controller to user units and post the arrays */ + for (j=0; jreadbackProfile(); + } + readbackStatus = readbackOK ? PROFILE_STATUS_SUCCESS : PROFILE_STATUS_FAILURE; + setIntegerParam(profileReadbackStatus_, readbackStatus); + setStringParam(profileReadbackMessage_, message); + if (!readbackOK) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: %s\n", - driverName, functionName, readbackMessage); + driverName, functionName, message); } - /* Clear readback command. This is a "busy" record, don't want to do this until build is complete. */ + /* Clear readback command. This is a "busy" record, don't want to do this until readback is complete. */ setIntegerParam(profileReadback_, 0); setIntegerParam(profileReadbackState_, PROFILE_READBACK_DONE); callParamCallbacks(); return status ? asynError : asynSuccess; } -asynStatus XPSController::enableSetPosition(int enable) -{ - enableSetPosition_ = enable; - return asynSuccess; -} - -/** - * Function to set the seetling time used when setting the XPS position. - * The sleep is performed after the axes are initialised, to take account of any - * post initialisation wobble. - * @param settlingTime The time in seconds to sleep. - */ -asynStatus XPSController::setPositionSettlingTime(double settlingTime) -{ - setPositionSettlingTime_ = settlingTime; - return asynSuccess; -} - /** The following functions have C linkage, and can be called directly or from iocsh */