Files
sinqMotor/src/sinqAxis.cpp
2025-02-14 16:19:17 +01:00

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