732 lines
30 KiB
C++
732 lines
30 KiB
C++
#include "sinqAxis.h"
|
|
#include "epicsExport.h"
|
|
#include "iocsh.h"
|
|
#include "sinqController.h"
|
|
#include <errlog.h>
|
|
#include <math.h>
|
|
#include <unistd.h>
|
|
|
|
sinqAxis::sinqAxis(class sinqController *pC, int axisNo)
|
|
: asynMotorAxis((asynMotorController *)pC, axisNo), pC_(pC) {
|
|
asynStatus status = asynSuccess;
|
|
|
|
initial_poll_ = true;
|
|
watchdogMovActive_ = false;
|
|
init_poll_counter_ = 0;
|
|
scaleMovTimeout_ = 2.0;
|
|
offsetMovTimeout_ = 30;
|
|
targetPosition_ = 0.0;
|
|
|
|
// Motor is assumed to be enabled
|
|
status = setIntegerParam(pC_->motorEnableRBV_, 1);
|
|
if (status != asynSuccess) {
|
|
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\", axis %d => %s, line %d:\nFATAL ERROR "
|
|
"(setting a parameter value failed "
|
|
"with %s)\n. Terminating IOC",
|
|
pC->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
pC_->stringifyAsynStatus(status));
|
|
exit(-1);
|
|
}
|
|
|
|
// By default, motors cannot be disabled
|
|
status = setIntegerParam(pC_->motorCanDisable_, 0);
|
|
if (status != asynSuccess) {
|
|
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\", axis %d => %s, line %d:\nFATAL ERROR "
|
|
"(setting a parameter value failed "
|
|
"with %s)\n. Terminating IOC",
|
|
pC->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
pC_->stringifyAsynStatus(status));
|
|
exit(-1);
|
|
}
|
|
|
|
// Provide a default value for the motor position.
|
|
status = setDoubleParam(pC_->motorPosition_, 0.0);
|
|
if (status != asynSuccess) {
|
|
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\", axis %d => %s, line %d:\nFATAL ERROR "
|
|
"(setting a parameter value failed "
|
|
"with %s)\n. Terminating IOC",
|
|
pC->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
pC_->stringifyAsynStatus(status));
|
|
exit(-1);
|
|
}
|
|
|
|
// We assume that the motor has no status problems initially
|
|
status = setIntegerParam(pC_->motorStatusProblem_, 0);
|
|
if (status != asynSuccess) {
|
|
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\", axis %d => %s, line %d:\nFATAL ERROR "
|
|
"(setting a parameter value failed "
|
|
"with %s)\n. Terminating IOC",
|
|
pC->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
pC_->stringifyAsynStatus(status));
|
|
exit(-1);
|
|
}
|
|
|
|
// Set the homing-related flags
|
|
status = setIntegerParam(pC_->motorStatusHome_, 0);
|
|
if (status != asynSuccess) {
|
|
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\", axis %d => %s, line %d:\nFATAL ERROR "
|
|
"(setting a parameter value failed "
|
|
"with %s)\n. Terminating IOC",
|
|
pC->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
pC_->stringifyAsynStatus(status));
|
|
exit(-1);
|
|
}
|
|
status = setIntegerParam(pC_->motorStatusHomed_, 0);
|
|
if (status != asynSuccess) {
|
|
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\", axis %d => %s, line %d:\nFATAL ERROR "
|
|
"(setting a parameter value failed "
|
|
"with %s)\n. Terminating IOC",
|
|
pC->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
pC_->stringifyAsynStatus(status));
|
|
exit(-1);
|
|
}
|
|
status = setIntegerParam(pC_->motorStatusAtHome_, 0);
|
|
if (status != asynSuccess) {
|
|
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\", axis %d => %s, line %d:\nFATAL ERROR "
|
|
"(setting a parameter value failed "
|
|
"with %s)\n. Terminating IOC",
|
|
pC->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
pC_->stringifyAsynStatus(status));
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
asynStatus sinqAxis::poll(bool *moving) {
|
|
// Local variable declaration
|
|
asynStatus pl_status = asynSuccess;
|
|
asynStatus poll_status = asynSuccess;
|
|
int homing = 0;
|
|
int homed = 0;
|
|
|
|
/*
|
|
At the beginning of the poll, it is assumed that the axis has no status
|
|
problems and therefore all error indicators are reset. This does not affect
|
|
the PVs until callParamCallbacks has been called!
|
|
|
|
The motorStatusProblem_ field changes the motor record fields SEVR and STAT.
|
|
*/
|
|
pl_status = setIntegerParam(pC_->motorStatusProblem_, false);
|
|
if (pl_status != asynSuccess) {
|
|
pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_", axisNo_,
|
|
__PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
pl_status = setIntegerParam(pC_->motorStatusCommsError_, false);
|
|
if (pl_status != asynSuccess) {
|
|
pC_->paramLibAccessFailed(pl_status, "motorStatusCommsError_", axisNo_,
|
|
__PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
pl_status = setStringParam(pC_->motorMessageText_, "");
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
|
|
// The poll function is just a wrapper around doPoll and
|
|
// handles mainly the callParamCallbacks() function. This wrapper is used
|
|
// to make sure callParamCallbacks() is called in case of a premature
|
|
// return.
|
|
poll_status = doPoll(moving);
|
|
|
|
// The poll did not succeed: Something went wrong and the motor has a status
|
|
// problem.
|
|
if (poll_status != asynSuccess) {
|
|
pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
|
|
if (pl_status != asynSuccess) {
|
|
pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_", axisNo_,
|
|
__PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
}
|
|
|
|
pl_status = pC_->getIntegerParam(axisNo_, pC_->motorStatusHomed_, &homed);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorStatusHomed_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
pl_status = pC_->getIntegerParam(axisNo_, pC_->motorStatusHome_, &homing);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorStatusHome_", axisNo_,
|
|
__PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
|
|
if (homing == 1 && !(*moving)) {
|
|
|
|
// Set the homing-related flags
|
|
pl_status = setIntegerParam(pC_->motorStatusHome_, 0);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorStatusHome_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
pl_status = setIntegerParam(pC_->motorStatusHomed_, 1);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorStatusHomed_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
pl_status = setIntegerParam(pC_->motorStatusAtHome_, 1);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorStatusAtHome_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
}
|
|
|
|
// Check and update the watchdog
|
|
if (checkMovTimeoutWatchdog(*moving) != asynSuccess) {
|
|
return asynError;
|
|
}
|
|
|
|
// According to the function documentation of asynMotorAxis::poll, this
|
|
// function should be called at the end of a poll implementation.
|
|
pl_status = callParamCallbacks();
|
|
if (pl_status != asynSuccess) {
|
|
// If we can't communicate with the parameter library, it doesn't make
|
|
// sense to try and upstream this to the user -> Just log the error
|
|
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\", axis %d => %s, line "
|
|
"%d:\ncallParamCallbacks failed with %s.\n",
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
pC_->stringifyAsynStatus(poll_status));
|
|
poll_status = pl_status;
|
|
}
|
|
|
|
return poll_status;
|
|
}
|
|
|
|
asynStatus sinqAxis::doPoll(bool *moving) { return asynSuccess; }
|
|
|
|
asynStatus sinqAxis::move(double position, int relative, double minVelocity,
|
|
double maxVelocity, double acceleration) {
|
|
|
|
// Status of parameter library operations
|
|
asynStatus pl_status = asynSuccess;
|
|
double motorRecResolution = 0.0;
|
|
|
|
// =========================================================================
|
|
|
|
// When a new move is done, the motor is not homed anymore
|
|
pl_status = setIntegerParam(pC_->motorStatusHomed_, 0);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorStatusHomed_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
pl_status = setIntegerParam(pC_->motorStatusAtHome_, 0);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorStatusAtHome_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution_,
|
|
&motorRecResolution);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorRecResolution_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
|
|
// Store the target position internally
|
|
targetPosition_ = position * motorRecResolution;
|
|
|
|
return doMove(position, relative, minVelocity, maxVelocity, acceleration);
|
|
}
|
|
|
|
asynStatus sinqAxis::doMove(double position, int relative, double minVelocity,
|
|
double maxVelocity, double acceleration) {
|
|
return asynSuccess;
|
|
}
|
|
|
|
asynStatus sinqAxis::home(double minVelocity, double maxVelocity,
|
|
double acceleration, int forwards) {
|
|
|
|
asynStatus status = asynSuccess;
|
|
|
|
status = doHome(minVelocity, maxVelocity, acceleration, forwards);
|
|
|
|
if (status == asynSuccess) {
|
|
|
|
status = setStringParam(pC_->motorMessageText_, "Homing");
|
|
if (status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(status, "motorMessageText_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
|
|
// Set the homing-related flags
|
|
status = setIntegerParam(pC_->motorStatusHome_, 1);
|
|
if (status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(status, "motorStatusHome_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
status = setIntegerParam(pC_->motorStatusHomed_, 0);
|
|
if (status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(status, "motorStatusHomed_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
status = setIntegerParam(pC_->motorStatusAtHome_, 0);
|
|
if (status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(status, "motorStatusAtHome_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
|
|
// Update the motor record
|
|
return callParamCallbacks();
|
|
|
|
} else if (status == asynError) {
|
|
// asynError means that we tried to home an absolute encoder
|
|
status = setStringParam(pC_->motorMessageText_,
|
|
"Can't home a motor with absolute encoder");
|
|
if (status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(status, "motorMessageText_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
|
|
// Update the motor record
|
|
return callParamCallbacks();
|
|
} else {
|
|
// Bubble up all other problems
|
|
return status;
|
|
}
|
|
}
|
|
|
|
asynStatus sinqAxis::doHome(double minVelocity, double maxVelocity,
|
|
double acceleration, int forwards) {
|
|
return asynSuccess;
|
|
}
|
|
|
|
asynStatus sinqAxis::enable(bool on) { return asynSuccess; }
|
|
|
|
asynStatus sinqAxis::setVeloFields(double velo, double vbas, double vmax) {
|
|
asynStatus status = asynSuccess;
|
|
int variableSpeed = 0;
|
|
|
|
// Can the speed of the motor be varied?
|
|
status =
|
|
pC_->getIntegerParam(axisNo_, pC_->motorCanSetSpeed_, &variableSpeed);
|
|
if (status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(status, "motorCanSetSpeed_", axisNo_,
|
|
__PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
if (variableSpeed == 1) {
|
|
|
|
// Check the inputs and create corresponding error messages
|
|
if (vbas > vmax) {
|
|
asynPrint(pC_->lowLevelPortUser_, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\", axis %d => %s, line %d:\nLower speed "
|
|
"limit vbas=%lf must not be smaller than upper limit "
|
|
"vmax=%lf.\n",
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
vbas, vmax);
|
|
|
|
status = setStringParam(
|
|
pC_->motorMessageText_,
|
|
"Lower speed limit must not be smaller than upper speed limit");
|
|
if (status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(status, "motorMessageText_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
return asynError;
|
|
}
|
|
if (velo < vbas || velo > vmax) {
|
|
asynPrint(pC_->lowLevelPortUser_, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\", axis %d => %s, line %d:\nActual "
|
|
"speed velo=%lf must be between lower limit vbas=%lf and "
|
|
"upper limit vmax=%lf.\n",
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
velo, vbas, vmax);
|
|
|
|
status = setStringParam(pC_->motorMessageText_,
|
|
"Speed is not inside limits");
|
|
if (status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(status, "motorMessageText_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
return asynError;
|
|
}
|
|
|
|
status = pC_->setDoubleParam(axisNo_, pC_->motorVbasFromDriver_, vbas);
|
|
if (status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(status, "motorVbasFromDriver_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
|
|
status = pC_->setDoubleParam(axisNo_, pC_->motorVeloFromDriver_, velo);
|
|
if (status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(status, "motorVeloFromDriver_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
|
|
status = pC_->setDoubleParam(axisNo_, pC_->motorVmaxFromDriver_, vmax);
|
|
if (status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(status, "motorVmaxFromDriver_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
} else {
|
|
// Set minimum and maximum speed equal to the set speed
|
|
status = pC_->setDoubleParam(axisNo_, pC_->motorVbasFromDriver_, velo);
|
|
if (status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(status, "motorVbasFromDriver_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
|
|
status = pC_->setDoubleParam(axisNo_, pC_->motorVeloFromDriver_, velo);
|
|
if (status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(status, "motorVeloFromDriver_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
|
|
status = pC_->setDoubleParam(axisNo_, pC_->motorVmaxFromDriver_, velo);
|
|
if (status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(status, "motorVmaxFromDriver_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
asynStatus sinqAxis::setAcclField(double accl) {
|
|
|
|
if (accl <= 0.0) {
|
|
return asynError;
|
|
}
|
|
|
|
asynStatus status = setDoubleParam(pC_->motorAcclFromDriver_, accl);
|
|
if (status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(status, "motorAcclFromDriver_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
asynStatus sinqAxis::setWatchdogEnabled(bool enable) {
|
|
return setIntegerParam(pC_->motorEnableMovWatchdog_, enable);
|
|
}
|
|
|
|
asynStatus sinqAxis::startMovTimeoutWatchdog() {
|
|
asynStatus pl_status;
|
|
int enableMovWatchdog = 0;
|
|
|
|
pl_status = pC_->getIntegerParam(axisNo_, pC_->motorEnableMovWatchdog_,
|
|
&enableMovWatchdog);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorEnableMovWatchdog_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
|
|
if (enableMovWatchdog == 1) {
|
|
// These parameters are only needed in this branch
|
|
double motorPosition = 0.0;
|
|
double motorPositionRec = 0.0;
|
|
double motorVelocity = 0.0;
|
|
double motorVelocityRec = 0.0;
|
|
double motorAccel = 0.0;
|
|
double motorAccelRec = 0.0;
|
|
double motorRecResolution = 0.0;
|
|
time_t timeContSpeed = 0;
|
|
time_t timeAccel = 0;
|
|
|
|
// Activate the watchdog
|
|
watchdogMovActive_ = true;
|
|
|
|
/*
|
|
NOTE: This function must not be called in the constructor (e.g. in order
|
|
to save the read result to the member variable earlier), since the
|
|
parameter library is updated at a later stage!
|
|
*/
|
|
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution_,
|
|
&motorRecResolution);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorRecResolution_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
|
|
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorPosition_,
|
|
&motorPositionRec);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorPosition",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
|
|
motorPosition = motorPositionRec * motorRecResolution;
|
|
|
|
/*
|
|
We use motorVelocity, which corresponds to the record field VELO.
|
|
From https://epics.anl.gov/docs/APS2015/14-Motor-Record.pdf:
|
|
* VELO = motorVelocity_ = Slew velocity
|
|
* VBAS = motorVelBase_ = Only used for stepper motors to minimize
|
|
resonance.
|
|
As documented in
|
|
https://epics.anl.gov/docs/APS2015/17-Motor-Driver.pdf, the
|
|
following relations apply: motorVelBase = VBAS / MRES motorVelocity
|
|
= VELO / MRES motorAccel = (motorVelocity - motorVelBase) / ACCL
|
|
Therefore, we need to correct the values from the parameter library.
|
|
*/
|
|
|
|
// Read the velocity
|
|
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorVelocity_,
|
|
&motorVelocityRec);
|
|
|
|
// Only calculate timeContSpeed if the motorVelocity has been populated
|
|
// with a sensible value (e.g. > 0)
|
|
if (pl_status == asynSuccess && motorVelocityRec > 0.0) {
|
|
// Convert back to the value in the VELO field
|
|
motorVelocity = motorVelocityRec * motorRecResolution;
|
|
if (pl_status == asynSuccess) {
|
|
|
|
timeContSpeed = std::ceil(
|
|
std::fabs(targetPosition_ - motorPosition) / motorVelocity);
|
|
}
|
|
}
|
|
|
|
pl_status =
|
|
pC_->getDoubleParam(axisNo_, pC_->motorAccel_, &motorAccelRec);
|
|
if (pl_status == asynSuccess && motorVelocityRec > 0.0 &&
|
|
motorAccelRec > 0.0) {
|
|
|
|
// Convert back to the value in the ACCL field
|
|
motorAccel = motorVelocityRec / motorAccelRec;
|
|
|
|
// Calculate the time
|
|
timeAccel = 2 * std::ceil(motorVelocity / motorAccel);
|
|
}
|
|
|
|
// Calculate the expected arrival time
|
|
expectedArrivalTime_ =
|
|
time(NULL) + offsetMovTimeout_ +
|
|
scaleMovTimeout_ * (timeContSpeed + 2 * timeAccel);
|
|
} else {
|
|
watchdogMovActive_ = false;
|
|
}
|
|
return asynSuccess;
|
|
}
|
|
|
|
asynStatus sinqAxis::checkMovTimeoutWatchdog(bool moving) {
|
|
asynStatus pl_status;
|
|
int enableMovWatchdog = 0;
|
|
|
|
pl_status = pC_->getIntegerParam(axisNo_, pC_->motorEnableMovWatchdog_,
|
|
&enableMovWatchdog);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorEnableMovWatchdog_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
|
|
// Not moving or watchdog not active / enabled
|
|
if (enableMovWatchdog == 0 || !moving || !watchdogMovActive_) {
|
|
watchdogMovActive_ = false;
|
|
return asynSuccess;
|
|
}
|
|
|
|
// Check if the expected time of arrival has been exceeded.
|
|
if (expectedArrivalTime_ < time(NULL)) {
|
|
// Check the watchdog
|
|
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\", axis %d => %s, line %d:\nExceeded "
|
|
"expected arrival time %ld (current time is %ld).\n",
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
expectedArrivalTime_, time(NULL));
|
|
|
|
pl_status = setStringParam(
|
|
pC_->motorMessageText_,
|
|
"Exceeded expected arrival time. Check if the axis is blocked.");
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
|
|
pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
|
|
if (pl_status != asynSuccess) {
|
|
pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_", axisNo_,
|
|
__PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
|
|
// Even if the movement timed out, the rest of the poll should continue.
|
|
return asynSuccess;
|
|
}
|
|
return asynSuccess;
|
|
}
|
|
|
|
// =============================================================================
|
|
// IOC shell functions
|
|
extern "C" {
|
|
|
|
/**
|
|
* @brief Enable / disable the watchdog (FFI implementation)
|
|
*
|
|
* @param portName Name of the controller
|
|
* @param axisNo Axis number
|
|
* @param enable If 0, disable the watchdog, otherwise enable
|
|
* it
|
|
* @return asynStatus
|
|
*/
|
|
asynStatus setWatchdogEnabled(const char *portName, int axisNo, int enable) {
|
|
|
|
sinqController *pC;
|
|
pC = (sinqController *)findAsynPortDriver(portName);
|
|
if (pC == nullptr) {
|
|
errlogPrintf("Controller \"%s\" => %s, line %d:\nPort %s not found.",
|
|
portName, __PRETTY_FUNCTION__, __LINE__, portName);
|
|
return asynError;
|
|
}
|
|
|
|
asynMotorAxis *asynAxis = pC->getAxis(axisNo);
|
|
sinqAxis *axis = dynamic_cast<sinqAxis *>(asynAxis);
|
|
if (axis == nullptr) {
|
|
errlogPrintf(
|
|
"Controller \"%s\", axis %d => %s, line %d:\nAxis does not "
|
|
"exist or is not an instance of sinqAxis.",
|
|
portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
|
|
return axis->setWatchdogEnabled(enable != 0);
|
|
}
|
|
|
|
static const iocshArg setWatchdogEnabledArg0 = {"Controller port name",
|
|
iocshArgString};
|
|
static const iocshArg setWatchdogEnabledArg1 = {"Axis number", iocshArgInt};
|
|
static const iocshArg setWatchdogEnabledArg2 = {
|
|
"Enabling / disabling the watchdog", iocshArgInt};
|
|
static const iocshArg *const setWatchdogEnabledArgs[] = {
|
|
&setWatchdogEnabledArg0, &setWatchdogEnabledArg1, &setWatchdogEnabledArg2};
|
|
static const iocshFuncDef setWatchdogEnabledDef = {"setWatchdogEnabled", 3,
|
|
setWatchdogEnabledArgs};
|
|
|
|
static void setWatchdogEnabledCallFunc(const iocshArgBuf *args) {
|
|
setWatchdogEnabled(args[0].sval, args[1].ival, args[2].ival);
|
|
}
|
|
|
|
// =============================================================================
|
|
|
|
/**
|
|
* @brief Set the offsetMovTimeout (FFI implementation)
|
|
*
|
|
* @param portName Name of the controller
|
|
* @param axisNo Axis number
|
|
* @param offsetMovTimeout Offset (in seconds)
|
|
* @return asynStatus
|
|
*/
|
|
asynStatus setOffsetMovTimeout(const char *portName, int axisNo,
|
|
double offsetMovTimeout) {
|
|
|
|
sinqController *pC;
|
|
pC = (sinqController *)findAsynPortDriver(portName);
|
|
if (pC == nullptr) {
|
|
errlogPrintf("Controller \"%s\" => %s, line %d:\nPort %s not found.",
|
|
portName, __PRETTY_FUNCTION__, __LINE__, portName);
|
|
return asynError;
|
|
}
|
|
|
|
asynMotorAxis *asynAxis = pC->getAxis(axisNo);
|
|
sinqAxis *axis = dynamic_cast<sinqAxis *>(asynAxis);
|
|
if (axis == nullptr) {
|
|
errlogPrintf("Controller \"%s\" => %s, line %d:\nAxis %d does not "
|
|
"exist or is not an "
|
|
"instance of sinqAxis.",
|
|
portName, __PRETTY_FUNCTION__, __LINE__, axisNo);
|
|
}
|
|
|
|
return axis->setOffsetMovTimeout(offsetMovTimeout);
|
|
}
|
|
|
|
static const iocshArg setOffsetMovTimeoutArg0 = {"Controller port name",
|
|
iocshArgString};
|
|
static const iocshArg setOffsetMovTimeoutArg1 = {"Axis number", iocshArgInt};
|
|
static const iocshArg setOffsetMovTimeoutArg2 = {"Offset timeout for movement",
|
|
iocshArgDouble};
|
|
static const iocshArg *const setOffsetMovTimeoutArgs[] = {
|
|
&setOffsetMovTimeoutArg0, &setOffsetMovTimeoutArg1,
|
|
&setOffsetMovTimeoutArg2};
|
|
static const iocshFuncDef setOffsetMovTimeoutDef = {"setOffsetMovTimeout", 3,
|
|
setOffsetMovTimeoutArgs};
|
|
|
|
static void setOffsetMovTimeoutCallFunc(const iocshArgBuf *args) {
|
|
setOffsetMovTimeout(args[0].sval, args[1].ival, args[2].dval);
|
|
}
|
|
|
|
// =============================================================================
|
|
|
|
/**
|
|
* @brief Set the setScaleMovTimeout (FFI implementation)
|
|
*
|
|
* @param portName Name of the controller
|
|
* @param axisNo Axis number
|
|
* @param scaleMovTimeout Scaling factor (in seconds)
|
|
* @return asynStatus
|
|
*/
|
|
asynStatus setScaleMovTimeout(const char *portName, int axisNo,
|
|
double scaleMovTimeout) {
|
|
|
|
sinqController *pC;
|
|
pC = (sinqController *)findAsynPortDriver(portName);
|
|
if (pC == nullptr) {
|
|
errlogPrintf("Controller \"%s\" => %s, line %d:\nPort %s not found.",
|
|
portName, __PRETTY_FUNCTION__, __LINE__, portName);
|
|
return asynError;
|
|
}
|
|
|
|
asynMotorAxis *asynAxis = pC->getAxis(axisNo);
|
|
sinqAxis *axis = dynamic_cast<sinqAxis *>(asynAxis);
|
|
if (axis == nullptr) {
|
|
errlogPrintf("Controller \"%s\" => %s, line %d:\nAxis %d does not "
|
|
"exist or is not an "
|
|
"instance of sinqAxis.",
|
|
portName, __PRETTY_FUNCTION__, __LINE__, axisNo);
|
|
}
|
|
|
|
return axis->setScaleMovTimeout(scaleMovTimeout);
|
|
}
|
|
|
|
static const iocshArg setScaleMovTimeoutArg0 = {"Controller port name",
|
|
iocshArgString};
|
|
static const iocshArg setScaleMovTimeoutArg1 = {"Axis number", iocshArgInt};
|
|
static const iocshArg setScaleMovTimeoutArg2 = {
|
|
"Multiplier for calculated move time", iocshArgDouble};
|
|
static const iocshArg *const setScaleMovTimeoutArgs[] = {
|
|
&setScaleMovTimeoutArg0, &setScaleMovTimeoutArg1, &setScaleMovTimeoutArg2};
|
|
static const iocshFuncDef setScaleMovTimeoutDef = {"setScaleMovTimeout", 3,
|
|
setScaleMovTimeoutArgs};
|
|
|
|
static void setScaleMovTimeoutCallFunc(const iocshArgBuf *args) {
|
|
setScaleMovTimeout(args[0].sval, args[1].ival, args[2].dval);
|
|
}
|
|
|
|
// =============================================================================
|
|
|
|
// This function is made known to EPICS in sinqMotor.dbd and is called by EPICS
|
|
// in order to register all functions in the IOC shell
|
|
static void sinqAxisRegister(void) {
|
|
iocshRegister(&setOffsetMovTimeoutDef, setOffsetMovTimeoutCallFunc);
|
|
iocshRegister(&setScaleMovTimeoutDef, setScaleMovTimeoutCallFunc);
|
|
iocshRegister(&setWatchdogEnabledDef, setWatchdogEnabledCallFunc);
|
|
}
|
|
epicsExportRegistrar(sinqAxisRegister);
|
|
|
|
} // extern C
|