Files
motorBase/motorApp/NewportSrc/HXPDriver.cpp
T

571 lines
20 KiB
C++

/*
FILENAME... HXPDriver.cpp
USAGE... Motor driver support for the Newport Hexapod controller.
Note: This driver was tested with the v1.3.x of the firmware
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <iocsh.h>
#include <epicsThread.h>
#include <epicsString.h>
#include <asynOctetSyncIO.h>
#include "HXPDriver.h"
#include "hxp_drivers.h"
#include <epicsExport.h>
#define NINT(f) (int)((f)>0 ? (f)+0.5 : (f)-0.5)
#define NUM_AXES 6
#define GROUP "HEXAPOD"
#define MRES 0.00001
static const char *driverName = "HXPDriver";
/** Creates a new HXPController object.
* \param[in] portName The name of the asyn port that will be created for this driver
* \param[in] IPAddress The Newport hexapod controller's ip address
* \param[in] IPPort TCP/IP port used to communicate with the hexapod controller
* \param[in] movingPollPeriod The time between polls when any axis is moving
* \param[in] idlePollPeriod The time between polls when no axis is moving
*/
HXPController::HXPController(const char *portName, const char *IPAddress, int IPPort,
double movingPollPeriod, double idlePollPeriod)
: asynMotorController(portName, NUM_AXES, NUM_HXP_PARAMS,
0, // No additional interfaces beyond those in base class
0, // No additional callback interfaces beyond those in base class
ASYN_CANBLOCK | ASYN_MULTIDEVICE,
1, // autoconnect
0, 0) // Default priority and stack size
{
int axis;
HXPAxis *pAxis;
static const char *functionName = "HXPController::HXPController";
axisNames_ = epicsStrDup("XYZUVW");
IPAddress_ = epicsStrDup(IPAddress);
IPPort_ = IPPort;
createParam(HXPMoveCoordSysString, asynParamInt32, &HXPMoveCoordSys_);
createParam(HXPStatusString, asynParamInt32, &HXPStatus_);
createParam(HXPErrorString, asynParamInt32, &HXPError_);
createParam(HXPErrorDescString, asynParamOctet, &HXPErrorDesc_);
createParam(HXPMoveAllString, asynParamInt32, &HXPMoveAll_);
createParam(HXPMoveAllTargetXString, asynParamFloat64, &HXPMoveAllTargetX_);
createParam(HXPMoveAllTargetYString, asynParamFloat64, &HXPMoveAllTargetY_);
createParam(HXPMoveAllTargetZString, asynParamFloat64, &HXPMoveAllTargetZ_);
createParam(HXPMoveAllTargetUString, asynParamFloat64, &HXPMoveAllTargetU_);
createParam(HXPMoveAllTargetVString, asynParamFloat64, &HXPMoveAllTargetV_);
createParam(HXPMoveAllTargetWString, asynParamFloat64, &HXPMoveAllTargetW_);
// This socket is used for polling by the controller and all axes
pollSocket_ = HXPTCP_ConnectToServer((char *)IPAddress, IPPort, HXP_POLL_TIMEOUT);
if (pollSocket_ < 0) {
printf("%s:%s: error calling TCP_ConnectToServer for pollSocket\n",
driverName, functionName);
}
HXPFirmwareVersionGet(pollSocket_, firmwareVersion_);
for (axis=0; axis<NUM_AXES; axis++) {
pAxis = new HXPAxis(this, axis);
}
startPoller(movingPollPeriod, idlePollPeriod, 2);
}
/** Creates a new HXPController object.
* Configuration command, called directly or from iocsh
* \param[in] portName The name of the asyn port that will be created for this driver
* \param[in] HXPPortName The name of the drvAsynIPPPort that was created previously to connect to the Newport hexapod controller
* \param[in] numAxes The number of axes that this controller supports
* \param[in] movingPollPeriod The time in ms between polls when any axis is moving
* \param[in] idlePollPeriod The time in ms between polls when no axis is moving
*/
extern "C" int HXPCreateController(const char *portName, const char *IPAddress, int IPPort,
int movingPollPeriod, int idlePollPeriod)
{
HXPController *pHXPController
= new HXPController(portName, IPAddress, IPPort, movingPollPeriod/1000., idlePollPeriod/1000.);
pHXPController = NULL;
return(asynSuccess);
}
/** Reports on status of the driver
* \param[in] fp The file pointer on which report information will be written
* \param[in] level The level of report detail desired
*
* If details > 0 then information is printed about each axis.
* After printing controller-specific information it calls asynMotorController::report()
*/
void HXPController::report(FILE *fp, int level)
{
int coordSys;
getIntegerParam(HXPMoveCoordSys_, &coordSys);
fprintf(fp, "Newport hexapod motor driver %s, numAxes=%d, moving poll period=%f, idle poll period=%f, coordSys=%d\n",
this->portName, NUM_AXES, movingPollPeriod_, idlePollPeriod_, coordSys);
// Call the base class method
asynMotorController::report(fp, level);
}
/** Returns a pointer to an HXPAxis object.
* Returns NULL if the axis number encoded in pasynUser is invalid.
* \param[in] pasynUser asynUser structure that encodes the axis index number. */
HXPAxis* HXPController::getAxis(asynUser *pasynUser)
{
return static_cast<HXPAxis*>(asynMotorController::getAxis(pasynUser));
}
/** Returns a pointer to an HXPAxis object.
* Returns NULL if the axis number encoded in pasynUser is invalid.
* \param[in] axisNo Axis index number. */
HXPAxis* HXPController::getAxis(int axisNo)
{
return static_cast<HXPAxis*>(asynMotorController::getAxis(axisNo));
}
/** Called when asyn clients call pasynInt32->write().
* Extracts the function and axis number from pasynUser.
* Sets the value in the parameter library.
* If the function is HXPMoveAll_ then it calls moves all motors with a single command
* For all other functions it calls asynMotorController::writeInt32.
* Calls any registered callbacks for this pasynUser->reason and address.
* \param[in] pasynUser asynUser structure that encodes the reason and address.
* \param[in] value Value to write. */
asynStatus HXPController::writeInt32(asynUser *pasynUser, epicsInt32 value)
{
int function = pasynUser->reason;
asynStatus status = asynSuccess;
HXPAxis *pAxis = getAxis(pasynUser);
static const char *functionName = "writeInt32";
/* 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 = setIntegerParam(pAxis->axisNo_, function, value);
if (function == HXPMoveAll_)
{
/* if value == 1: motors are moved
if value == 0: nothing is done and parameter is simply reset */
if (value == 1)
{
moveAll(pAxis);
}
}
else
{
/* Call base class method */
status = asynMotorController::writeInt32(pasynUser, value);
}
/* Do callbacks so higher layers see any changes */
callParamCallbacks(pAxis->axisNo_);
if (status)
asynPrint(pasynUser, ASYN_TRACE_ERROR,
"%s:%s: error, status=%d function=%d, value=%d\n",
driverName, functionName, status, function, value);
else
asynPrint(pasynUser, ASYN_TRACEIO_DRIVER,
"%s:%s: function=%d, value=%d\n",
driverName, functionName, function, value);
return status;
}
/**
* Moves all hexpod axes to new target positions
*/
int HXPController::moveAll(HXPAxis *pAxis)
{
int status;
double x, y, z, u, v, w;
getDoubleParam(0, HXPMoveAllTargetX_, &x);
getDoubleParam(0, HXPMoveAllTargetY_, &y);
getDoubleParam(0, HXPMoveAllTargetZ_, &z);
getDoubleParam(0, HXPMoveAllTargetU_, &u);
getDoubleParam(0, HXPMoveAllTargetV_, &v);
getDoubleParam(0, HXPMoveAllTargetW_, &w);
status = HXPHexapodMoveAbsolute(pAxis->moveSocket_, GROUP, "Work", x, y, z, u, v, w);
/* This is similar to what is done in HXPAxis::move() */
if (status < 0)
{
/* Set the error */
setIntegerParam(HXPError_, status);
/* Get the error string */
HXPErrorStringGet(pAxis->moveSocket_, status, pAxis->errorDescFull_);
/* Trim the error string */
strncpy(pAxis->errorDesc_, pAxis->errorDescFull_, 39);
pAxis->errorDesc_[39] = 0;
/* Set the error description */
setStringParam(HXPErrorDesc_, pAxis->errorDesc_);
}
else
{
/* Clear the error */
setIntegerParam(HXPError_, 0);
setStringParam(HXPErrorDesc_, "");
/* If there was a way to force the motor record to switch to the move poll rate,
this would be the place to do it. */
}
callParamCallbacks();
return status;
}
// These are the HXPAxis methods
/** Creates a new HXPAxis object.
* \param[in] pC Pointer to the HXPController to which this axis belongs.
* \param[in] axisNo Index number of this axis, range 0 to pC->numAxes_-1.
*
* Initializes register numbers, etc.
*/
HXPAxis::HXPAxis(HXPController *pC, int axisNo)
: asynMotorAxis(pC, axisNo),
pC_(pC)
{
axisName_ = pC_->axisNames_[axisNo];
sprintf(positionerName_, "%s.%c", GROUP, (char) axisName_);
// Couldn't a negative timeout be used here instead?
moveSocket_ = HXPTCP_ConnectToServer(pC_->IPAddress_, pC->IPPort_, HXP_POLL_TIMEOUT);
/* Set the poll rate on the moveSocket to a negative number, which means that SendAndReceive should do only a write, no read */
HXPTCP_SetTimeout(moveSocket_, -0.1);
pollSocket_ = pC_->pollSocket_;
/* Enable gain support so that the CNEN field to enable/disable the hexapod */
setIntegerParam(pC_->motorStatusGainSupport_, 1);
// does the hexapod read encoders from the stages? leave in for now to test relative moves
setIntegerParam(pC_->motorStatusHasEncoder_, 1);
}
/** Reports on status of the axis
* \param[in] fp The file pointer on which report information will be written
* \param[in] level The level of report detail desired
*
* After printing device-specific information calls asynMotorAxis::report()
*/
void HXPAxis::report(FILE *fp, int level)
{
if (level > 0) {
fprintf(fp, " axis %d\n", axisNo_);
fprintf(fp, " axisName %c\n", axisName_);
}
// Call the base class method
asynMotorAxis::report(fp, level);
}
asynStatus HXPAxis::move(double position, int relative, double baseVelocity, double slewVelocity, double acceleration)
{
int status;
double start_pos;
double end_pos;
double diff_pos;
double cur_pos[NUM_AXES];
double rel_pos[NUM_AXES] = {};
double *pos;
int coordSys; // 0 = work, 1 = tool
static const char *functionName = "HXPAxis::move";
asynStatus retval = asynSuccess;
pC_->getIntegerParam(pC_->HXPMoveCoordSys_, &coordSys);
if (relative) {
rel_pos[axisNo_] = position * MRES;
pos = rel_pos;
if (coordSys == 0)
{
status = HXPHexapodMoveIncremental(moveSocket_, GROUP, "Work", pos[0], pos[1], pos[2], pos[3], pos[4], pos[5]);
}
else
{
status = HXPHexapodMoveIncremental(moveSocket_, GROUP, "Tool", pos[0], pos[1], pos[2], pos[3], pos[4], pos[5]);
}
if (status != 0 && status != -27) {
asynPrint(pasynUser_, ASYN_TRACE_ERROR,
"%s:%s: Error performing HexapodMoveIncremental[%s,%d] %d\n",
driverName, functionName, pC_->portName, axisNo_, status);
/* Error -27 is caused when the motor record changes dir i.e. when it aborts a move! */
retval = asynError;
}
} else {
// get current positions before moving (could an error overflow the array?)
status = HXPGroupPositionCurrentGet(pollSocket_, GROUP, 6, (double *) &cur_pos);
// Capture current position for relative move calc
start_pos = cur_pos[axisNo_];
end_pos = position * MRES;
if (coordSys == 0)
{
// Update position of axis to be moved
cur_pos[axisNo_] = end_pos;
pos = cur_pos;
status = HXPHexapodMoveAbsolute(moveSocket_, GROUP, "Work", pos[0], pos[1], pos[2], pos[3], pos[4], pos[5]);
}
else
{
// Calculate relative move amount (needed for Tool coord-sys moves)
diff_pos = end_pos - start_pos;
// Update position of axis to be moved
rel_pos[axisNo_] = diff_pos;
pos = rel_pos;
status = HXPHexapodMoveIncremental(moveSocket_, GROUP, "Tool", pos[0], pos[1], pos[2], pos[3], pos[4], pos[5]);
}
if (status != 0 && status != -27) {
asynPrint(pasynUser_, ASYN_TRACE_ERROR,
"%s:%s: Error performing HexapodMoveAbsolute[%s,%d] %d\n",
driverName, functionName, pC_->portName, axisNo_, status);
/* Error -27 is caused when the motor record changes dir i.e. when it aborts a move! */
retval = asynError;
}
}
if (status < 0)
{
/* Set the error */
pC_->setIntegerParam(pC_->HXPError_, status);
/* Get the error string */
HXPErrorStringGet(moveSocket_, status, errorDescFull_);
/* Trim the error string */
strncpy(errorDesc_, errorDescFull_, 39);
errorDesc_[39] = 0;
/* Set the error description */
pC_->setStringParam(pC_->HXPErrorDesc_, errorDesc_);
}
else
{
/* Clear the error */
pC_->setIntegerParam(pC_->HXPError_, 0);
pC_->setStringParam(pC_->HXPErrorDesc_, "");
}
callParamCallbacks();
return retval;
}
asynStatus HXPAxis::home(double baseVelocity, double slewVelocity, double acceleration, int forwards)
{
// static const char *functionName = "HXPAxis::home";
// kill all
HXPGroupKill(moveSocket_, GROUP);
// initialize
HXPGroupInitialize(moveSocket_, GROUP);
// home
HXPGroupHomeSearch(moveSocket_, GROUP);
return asynSuccess;
}
asynStatus HXPAxis::stop(double acceleration )
{
int status;
//static const char *functionName = "HXPAxis::stop";
status = HXPGroupMoveAbort(moveSocket_, GROUP);
return asynSuccess;
}
asynStatus HXPAxis::setClosedLoop(bool closedLoop)
{
int status;
static const char *functionName = "HXPAxis::setClosedLoop";
if (closedLoop) {
status = HXPGroupMotionEnable(pollSocket_, GROUP);
if (status) {
asynPrint(pasynUser_, ASYN_TRACE_ERROR,
"%s:%s: [%s,%d]: error calling GroupMotionEnable status=%d\n",
driverName, functionName, pC_->portName, axisNo_, status);
} else {
asynPrint(pasynUser_, ASYN_TRACE_FLOW,
"%s:%s: set XPS %s, axis %d closed loop enable\n",
driverName, functionName, pC_->portName, axisNo_);
}
} else {
status = HXPGroupMotionDisable(pollSocket_, GROUP);
if (status) {
asynPrint(pasynUser_, ASYN_TRACE_ERROR,
"%s:%s: [%s,%d]: error calling GroupMotionDisable status=%d\n",
driverName, functionName, pC_->portName, axisNo_, status);
} else {
asynPrint(pasynUser_, ASYN_TRACE_FLOW,
"%s:%s: motorAxisSetInteger set XPS %s, axis %d closed loop disable\n",
driverName, functionName, pC_->portName, axisNo_);
}
}
return (asynStatus)status;
}
/** Polls the axis.
* This function reads the motor position, the limit status, the home status, the moving status,
* and the drive power-on status.
* It calls setIntegerParam() and setDoubleParam() for each item that it polls,
* and then calls callParamCallbacks() at the end.
* \param[out] moving A flag that is set indicating that the axis is moving (true) or done (false). */
asynStatus HXPAxis::poll(bool *moving)
{
int status;
//char readResponse[25];
static const char *functionName = "HXPAxis::poll";
status = HXPGroupStatusGet(pollSocket_,
GROUP,
&axisStatus_);
if (status) {
asynPrint(pasynUser_, ASYN_TRACE_ERROR,
"%s:%s: [%s,%d]: error calling GroupStatusGet status=%d; pollSocket=%d\n",
driverName, functionName, pC_->portName, axisNo_, status, pollSocket_);
goto done;
}
asynPrint(pasynUser_, ASYN_TRACE_FLOW,
"%s:%s: [%s,%d]: %s axisStatus=%d\n",
driverName, functionName, pC_->portName, axisNo_, positionerName_, axisStatus_);
/* Set the status */
/* Note: there is only one status PV for the controller. Currently it reflects the status of axis 0.
It might be better to call pC_->setIntegerParam(pC_->HXPStatus_, axisStatus_); */
setIntegerParam(pC_->HXPStatus_, axisStatus_);
/* If the group is not moving then the axis is not moving */
if ((axisStatus_ < 43) || (axisStatus_ > 48))
moving_ = false;
else
moving_ = true;
/* Set the axis done parameter */
*moving = moving_;
setIntegerParam(pC_->motorStatusDone_, *moving?0:1);
/*Test for states that mean we cannot move an axis (disabled, uninitialised, etc.)
and set problem bit in MSTA.*/
if ((axisStatus_ < 10) || ((axisStatus_ >= 20) && (axisStatus_ <= 42)) ||
(axisStatus_ == 50) || (axisStatus_ == 64))
{
/* Don't consider a normal disabled status to be a problem */
if ( axisStatus_==20 )
{
setIntegerParam(pC_->motorStatusProblem_, 0);
}
else
{
asynPrint(pasynUser_, ASYN_TRACE_FLOW,
"%s:%s: [%s,%d]: in unintialised/disabled/not referenced. XPS State Code: %d\n",
driverName, functionName, pC_->portName, axisNo_, axisStatus_);
setIntegerParam(pC_->motorStatusProblem_, 1);
}
/* Group status indicates power is off */
setIntegerParam(pC_->motorStatusPowerOn_, 0);
}
else
{
setIntegerParam(pC_->motorStatusProblem_, 0);
setIntegerParam(pC_->motorStatusPowerOn_, 1);
}
status = HXPGroupPositionCurrentGet(pollSocket_,
positionerName_,
1,
&encoderPosition_);
if (status) {
asynPrint(pasynUser_, ASYN_TRACE_ERROR,
"%s:%s: [%s,%d]: error calling GroupPositionCurrentGet status=%d\n",
driverName, functionName, pC_->portName, axisNo_, status);
goto done;
}
//setDoubleParam(pC_->motorEncoderPosition_, (encoderPosition_/stepSize_));
setDoubleParam(pC_->motorEncoderPosition_, encoderPosition_ / MRES);
status = HXPGroupPositionSetpointGet(pollSocket_,
positionerName_,
1,
&setpointPosition_);
if (status) {
asynPrint(pasynUser_, ASYN_TRACE_ERROR,
"%s:%s: [%s,%d]: error calling GroupPositionSetpointGet status=%d\n",
driverName, functionName, pC_->portName, axisNo_, status);
goto done;
}
//setDoubleParam(pC_->motorPosition_, (setpointPosition_/stepSize_));
setDoubleParam(pC_->motorPosition_, setpointPosition_ / MRES);
// limit check?
// dir flag?
done:
setIntegerParam(pC_->motorStatusProblem_, status ? 1:0);
callParamCallbacks();
return status ? asynError : asynSuccess;
}
/** Code for iocsh registration */
static const iocshArg HXPCreateControllerArg0 = {"Port name", iocshArgString};
static const iocshArg HXPCreateControllerArg1 = {"IP address", iocshArgString};
static const iocshArg HXPCreateControllerArg2 = {"Port", iocshArgInt};
static const iocshArg HXPCreateControllerArg3 = {"Moving poll period (ms)", iocshArgInt};
static const iocshArg HXPCreateControllerArg4 = {"Idle poll period (ms)", iocshArgInt};
static const iocshArg * const HXPCreateControllerArgs[] = {&HXPCreateControllerArg0,
&HXPCreateControllerArg1,
&HXPCreateControllerArg2,
&HXPCreateControllerArg3,
&HXPCreateControllerArg4};
static const iocshFuncDef HXPCreateControllerDef = {"HXPCreateController", 5, HXPCreateControllerArgs};
static void HXPCreateControllerCallFunc(const iocshArgBuf *args)
{
HXPCreateController(args[0].sval, args[1].sval, args[2].ival, args[3].ival, args[4].ival);
}
static void HXPRegister(void)
{
iocshRegister(&HXPCreateControllerDef, HXPCreateControllerCallFunc);
}
extern "C" {
epicsExportRegistrar(HXPRegister);
}