Files
sinqMotor/src/sinqAxis.h
smathis 86e6ab1fdd
All checks were successful
Test And Build / Lint (push) Successful in 6s
Test And Build / Build (push) Successful in 6s
Improved homing doc, fixed wrong setting of motorStatusHomed and added check to move
2026-03-10 13:17:45 +01:00

836 lines
32 KiB
C++

// SPDX-License-Identifier: GPL-3.0-only
/*
This class extends asynMotorAxis by some features used in SINQ.
Stefan Mathis, November 2024
*/
#ifndef sinqAxis_H
#define sinqAxis_H
// The EPICS libaries do not follow -Weffc++
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#include "asynMotorAxis.h"
#include "macros.h"
#pragma GCC diagnostic pop
#include <memory>
#include <type_traits>
struct sinqAxisImpl;
class HIDDEN sinqAxis : public asynMotorAxis {
public:
/**
* @brief Construct a new sinqAxis object
*
* @param pC_ Pointer to the controller of the axis
* @param axis Index of the axis
*/
sinqAxis(class sinqController *pC_, int axisNo);
/**
* @brief Destroy the sinqAxis object
*
* This destructor is necessary in order to use the PIMPL idiom.
*/
~sinqAxis();
/**
* @brief Delete the copy and copy assignment constructors, because this
* class should not be copied (it is tied to hardware!)
*/
sinqAxis(const sinqAxis &) = delete;
sinqAxis &operator=(const sinqAxis &) = delete;
/**
* @brief Check if a poll should be performed. If yes, call `forcedPoll`.
*
This is a wrapper around `forcedPoll` which does the following checks before
calling `forcedPoll`:
- Are there any outstanding fast polls (method `outstandingForcedFastPolls`
of the controller returns a value greater zero)?
- Was the axis moving last time its status was polled?
- Is adaptive polling disabled?
- Did an idle period pass since the last poll?
If all of these conditions are false, no poll is performed. Otherwise, the
`forcedPoll` method is called. This method should not be called in the
driver code itself if a poll is needed - use `forcedPoll` instead!
*
* @param moving Forwarded to `forcedPoll` or set to false
(depending on whether `forcedPoll was called`).
* @return asynStatus Forward the status of `forcedPoll` or set to
asynSuccess (depending on whether `forcedPoll was called`).
*/
virtual asynStatus poll(bool *moving);
/**
* @brief Perform some standardized operations before and after the concrete
`doPoll` implementation.
*
* Wrapper around `doPoll` which performs the following operations:
- Call the `doPoll` method
- Reset motorStatusProblem_, motorStatusCommsError_ and motorMessageText_ if
doPoll returned asynSuccess
- If the movement timeout watchdog has been started, check it.
- The flags `motorStatusHome_`, `motorStatusHomed_` and
`motorStatusAtHome_` are set to their idle values (0, 1 and 1 respectively)
in the `forcedPoll()` method once the homing procedure is finished. See the
documentation of the `home()` method for more details.
- Run `callParamCallbacks()`
- Return the status of `doPoll`
*
* @param moving Forwarded to `doPoll`.
* @return asynStatus Forward the status of `doPoll`, unless one of
the parameter library operation fails (in that case, returns the status of
the failed operation).
*/
asynStatus forcedPoll(bool *moving);
/**
* @brief Implementation of the "proper", device-specific poll method. This
method should be implemented by a child class of sinqAxis.
*
* @param moving Should be set to true, if the axis is moving,
* and false otherwise.
* @return asynStatus
*/
virtual asynStatus doPoll(bool *moving);
/**
* @brief Perform some standardized operations before and after the concrete
`doMove` implementation.
Wrapper around `doMove` which performs the following operations:
- If the motor has an incremental encoder and the paramLib entry for
motorStatusHomed returns 0, the move command is not forwarded if the motor
has an incremental encoder.
- It calculates the (absolute) target position and stores it in the member
variable `targetPosition_`. This member variable is e.g. used for the
movement watchdog.
Afterwards, it calls and returns `doMove`.
*
* @param position Forwarded to `doMove`.
* @param relative Forwarded to `doMove`.
* @param minVelocity Forwarded to `doMove`.
* @param maxVelocity Forwarded to `doMove`.
* @param acceleration Forwarded to `doMove`.
* @return asynStatus Forward the status of `doMove`, unless one of
the parameter library operation fails (in that case, returns the failed
operation status).
*/
virtual asynStatus move(double position, int relative, double minVelocity,
double maxVelocity, double acceleration);
/**
* @brief Implementation of the "proper", device-specific move method. This
method should be implemented by a child class of sinqAxis.
*
* @param position Target position `VAL` from the motor record
* @param relative Specifies, whether the target position is
relative or absolute.
* @param minVelocity Minimum velocity VMIN from the motor record
* @param maxVelocity Maximum velocity VMAX from the motor record
* @param acceleration Acceleration ACCEL from the motor record
* @return asynStatus
*/
virtual asynStatus doMove(double position, int relative, double minVelocity,
double maxVelocity, double acceleration);
/**
* @brief Wrapper around doHome which handles the homing-related flags
*
* The homing procedure of the motor record is controlled by the following
* parameter library flags:
*
* - `motorMoveToHome_`: Setting this flag to `1` indicates to EPICS that a
homing procedure should start and can therefore be used to start homing from
within the driver.
* - `motorStatusHome_`: This flag should be set to `1` while the motor is
actively moving toward its home position and to `0` otherwise.
* - `motorStatusAtHome_`: This flag should be set to `0` at the start of a
homing command and to 1 once the home position is reached. This is obviously
the case right after a homing run. Corresponds to bit 7 of the MSTA field of
the motor record.
* - `motorStatusHomed_`: This flag should be set to `1` if it is known that
that the motor has been homed since the last power cycle of the controller.
There are two ways the motor record can get this information:
1) If a home run has been performed by the driver itself via
`sinqAxis::home` (this function).
2) If the controller reports that the motor has been homed in the past
(possibly before the IOC startup). This information needs to be provided by
the controller (e.g. in an "init" function). Likewise, if the driver detects
a power cycle of the controller, the driver must set this field back to `0`.
* This function performs the following operations in the given order:
*
* - Call `doHome()` and forward the parameters
*
* - If `doHome()` returned asynSuccess: Set `motorStatusHome_` to `1`,
`motorStatusHomed_` to `0` and `motorStatusAtHome_` to `0`.
*
* - If `doHome()` returned asynError: This means that the motor cannot be
homed because the encoder is absolute. Set a corresponding error message,
but return asynSuccess in order to avoid any automatic retries by asyn.
* - If `doHome()` returned anything else: Forward the status.
*
* The flags `motorStatusHome_`, `motorStatusHomed_` and
`motorStatusAtHome_` are set to 0, 1 and 1 respectively in the
`forcedPoll())` method once the homing procedure is finished.
*
* @param minVelocity Forwarded to `doHome`.
* @param maxVelocity Forwarded to `doHome`.
* @param acceleration Forwarded to `doHome`.
* @param forwards Forwarded to `doHome`.
* @return asynStatus Forward the status of `doHome`, unless one of
the parameter library operation fails (in that case, returns the failed
operation status).
*/
virtual asynStatus home(double minVelocity, double maxVelocity,
double acceleration, int forwards);
/**
* @brief Implementation of the "proper", device-specific home method. This
method should be implemented by a child class of sinqAxis. If the motor
cannot be homed because it has an absolute encoder, this function should
return asynError.
*
* @param minVelocity Minimum velocity VMIN from the motor record
* @param maxVelocity Maximum velocity VMAX from the motor record
* @param acceleration Acceleration ACCEL from the motor record
* @param forwards Is 1, if the motor record field HOMF was used
to trigger the homing, and 0, if HOMR was used.
* @return asynStatus
*/
virtual asynStatus doHome(double minVelocity, double maxVelocity,
double acceleration, int forwards);
/**
* @brief This function is called when the PV "$(INSTR)$(M):Reset" is set to
* any value. It calls `doReset` (which ought to be implemented by a child
* class) and then performs da defined number of consecutive fast polls. If
* one of the polls returns asynSuccess, it returns immediately.
*
* @return asynStatus
*/
virtual asynStatus reset();
/**
* @brief Implementation of the "proper", device-specific `reset` method.
This method should be implemented by a child class of sinqAxis. If the
motor cannot be reset, this function should return asynError.
*
* @return asynStatus
*/
virtual asynStatus doReset();
/**
* @brief This function enables / disables an axis. It should be implemented
* by a child class of sinqAxis.
*
* The concrete implementation should (but doesn't need to) follow the
* convention that a value of 0 disables the axis and any other value
* enables it.
*
* @param on
* @return asynStatus
*/
virtual asynStatus enable(bool on);
/**
* @brief Populate the motor record fields VELO, VBAS and VMAX
*
* Populates the speed fields of the motor record. If the param lib
* entry motorCanSetSpeed_ (connected to the PV x:VariableSpeed) is set to
* 1, VBAS and VMAX are set to min and max respectively. Otherwise, they are
* set to val. Additionally, the speed itself is set to VELO.
*
* The units of the inputs are engineering units (EGU) per second (e.g. mm/s
* if the EGU is mm).
*
* If the given configuration is invalid (min > max, velo < min, velo > max)
* and the motor is configured as a variable speed motor (param lib entry
* motorCanSetSpeed_ is 1), this function returns an asynError.
*
* @param velo Actual velocity (EGU / s)
* @param vbas Minimum allowed velocity (EGU / s)
* @param velo Maximum allowed velocity (EGU / s)
*
* @return asynStatus
*/
virtual asynStatus setVeloFields(double velo, double vbas, double vmax);
/**
* @brief Populate the ACCL field of the motor record
*
* Populates the acceleration field of the motor record with the given
* value. If accl is not positive, this function does not set the value and
* returns an asynError.
*
* The unit of the input is engineering units (EGU) per second squared (e.g.
* mm/s^2 if the EGU is mm).
*
* @param accl Actual acceleration (EGU / s^2)
* @return asynStatus
*/
virtual asynStatus setAcclField(double accl);
/**
* @brief Start the watchdog for the movement, if the watchdog is not
disabled. See the documentation of checkMovTimeoutWatchdog for more details.
*
* @return asynStatus If one of the parameter library operations
used to get the values for the timeout calculation failed, return that
status, otherwise return asynSuccess.
*/
virtual asynStatus startMovTimeoutWatchdog();
/**
* @brief Check if the watchdog timed out
*
Manages a timeout mechanism for the movement:
If the axis is moving and the movement takes too long, create an error
message and return asynError. The watchdog is started when moving switches
from "false" to "true" and stopped when moving switches from "true" to
"false". At the watchdog start, the estimated movement time is calculated as
t = offsetMovTimeout_ + scaleMovTime_ * [timeContSpeed + 2*timeAccel]
with
timeContSpeed = abs(targetPosition - motorPosition) / motorVelBase
timeAcc = motorVelBase / motorAccel
The values motorVelBase, motorAccel and positionAtMovementStart are taken
from the parameter library. Therefore it is necessary to populate them
before using this function. If they are not given, both speed and velocity
are assumed to be infinite. This means that timeContSpeed and/or timeAcc are
set to zero. targetPosition is populated automatically when using the doMove
function.
The values offsetMovTimeout_ and scaleMovTimeout_ can be set directly from
the IOC shell with the functions setScaleMovTimeout and setOffsetMovTimeout,
if sinqMotor is loaded via the "require" mechanism.
*
* @param moving Should be the "moving" status from `poll` /
`doPoll`.
* @return asynStatus Return asynError, if the watchdog timed out,
and asynSuccess otherwise.
*/
virtual asynStatus checkMovTimeoutWatchdog(bool moving);
/**
* @brief Enable / disable the watchdog. Also available in the IOC shell
* (see "extern C" section in sinqAxis.cpp).
*
* If enable is set to false and the watchdog is currently running, this
* function stops it immediately.
*
* @param enabled
* @return asynStatus
*/
virtual asynStatus setWatchdogEnabled(bool enable);
/**
* @brief Set the offsetMovTimeout. Also available in the IOC shell
* (see "extern C" section in sinqAxis.cpp).
*
* See documentation of `checkMovTimeoutWatchdog` for details.
*
* @param offsetMovTimeout Offset (in seconds)
* @return asynStatus
*/
virtual asynStatus setOffsetMovTimeout(time_t offsetMovTimeout);
/**
* @brief Set the scaleMovTimeout. Also available in the IOC shell
* (see "extern C" section in sinqAxis.cpp).
*
See documentation of `checkMovTimeoutWatchdog` for details.
*
* @param scaleMovTimeout Scaling factor (in seconds)
* @return asynStatus
*/
virtual asynStatus setScaleMovTimeout(time_t scaleMovTimeout);
/**
* @brief Set the errorDisplayDuration. Also available in the IOC shell
* (see "extern C" section in sinqAxis.cpp).
*
This time in seconds define for which time an error will be displayed after
its root cause has been resolved.
*
* @param errorDisplayDuration Error display time (in seconds)
* @return asynStatus
*/
virtual asynStatus setErrorDisplayDuration(time_t errorDisplayDuration);
/**
* @brief Return the axis number of this axis
*
* @return int
*/
int axisNo() { return axisNo_; }
/**
* @brief Read the motor position from the paramLib, adjusted for the
* motorRecResolution
*
* The motorPosition value in the paramLib is the encoder position
* divided by the motorRecResolution (see README.md). This function
* fetches the paramLib value and multiplies it with motorRecResolution
* (also fetched from the paramLib).
*
* @param motorPositon
* @return asynStatus
*/
asynStatus motorPosition(double *motorPositon);
/**
* @brief Write the motor position in the paramLib, adjusted for the
* motorRecResolution
*
* The motorPosition value in the paramLib is the encoder position
* divided by the motorRecResolution (see README.md). This function takes
* the input value and divides it with motorRecResolution (fetched from
* the paramLib).
*
* @param motorPosition
* @return asynStatus
*/
asynStatus setMotorPosition(double motorPosition);
/**
* @brief Sanity-check the limits and write them into the database
*
* If the given `highLimit` is smaller than the `lowLimit`, the limits are
* set to `highLimit = motorPosition + motorRecResolution`,
* `lowLimit = motorPosition - motorRecResolution` and a warning is
* displayed in the IOC shell. This is not an error
*
* @param highLimit
* @param lowLimit
* @return asynStatus
*/
asynStatus setLimits(double highLimit, double lowLimit);
/**
* @brief Check if the axis is not connected and print a corresponding error
* message
*
* This method is meant to be used at the end of "interactive" function
* calls such as move, home, stop etc which can be manually triggered from
* the IOC shell or from the channel access protocol.
*/
asynStatus assertConnected();
/**
* @brief Return a pointer to the axis controller.
*
* This function should be overriden in derived classes using the `override`
* keyword so the macros `getAxisParamChecked` and `setAxisParamChecked`
* work correctly:
*
* ```
* class mySpecialAxis : public sinqAxis {
public:
mySpecialController* getControllerMethod() override {
return mySpecialControllerPtr;
}
};
* ```
*/
virtual sinqController *pController() { return pC_; };
/**
* @brief Returns true, if the axis was moving in the last poll cycle, and
* false otherwise.
*
* @return true
* @return false
*/
bool wasMoving();
/**
* @brief Override the wasMoving flag (normally, it is automatically updated
* during each poll).
*
*/
void setWasMoving(bool wasMoving);
/**
* @brief Read out the last received target position in engineering units.
*
* @return double
*/
double targetPosition();
/**
* @brief Override the targetPosition value (normally, it is automatically
* updated at every call of the move() method).
*
*/
void setTargetPosition(double targetPosition);
private:
// Ordering matters because pC_ is initialized before pSinqA_ in the
// constructor
sinqController *pC_;
std::unique_ptr<sinqAxisImpl> pSinqA_;
};
// =============================================================================
// Helper functions and definitions for the macro setAxisParamChecked
template <typename T> struct TypeTag {};
// Generic fallback - if the compiler tries to compile this function, it fails.
template <typename A, typename C, typename T>
asynStatus setAxisParamImpl(A *axis, C *controller, const char *indexName,
int (C::*func)(), T writeValue,
const char *callerFunctionName, int lineNumber,
TypeTag<void>) {
static_assert(sizeof(T) == 0, "Unsupported type for setAxisParamImpl");
return asynError;
}
template <typename A, typename C>
asynStatus setAxisParamImpl(A *axis, C *controller, const char *indexName,
int (C::*func)(), int writeValue,
const char *callerFunctionName, int lineNumber,
TypeTag<int>) {
int indexValue = (controller->*func)();
asynStatus status = axis->setIntegerParam(indexValue, writeValue);
if (status != asynSuccess) {
return controller->paramLibAccessFailed(
status, indexName, axis->axisNo(), callerFunctionName, lineNumber);
}
return asynSuccess;
}
template <typename A, typename C>
asynStatus setAxisParamImpl(A *axis, C *controller, const char *indexName,
int (C::*func)(), bool writeValue,
const char *callerFunctionName, int lineNumber,
TypeTag<bool>) {
return setAxisParamImpl(axis, controller, indexName, func,
static_cast<int>(writeValue), callerFunctionName,
lineNumber, TypeTag<int>{});
}
template <typename A, typename C>
asynStatus setAxisParamImpl(A *axis, C *controller, const char *indexName,
int (C::*func)(), double writeValue,
const char *callerFunctionName, int lineNumber,
TypeTag<double>) {
int indexValue = (controller->*func)();
asynStatus status = axis->setDoubleParam(indexValue, writeValue);
if (status != asynSuccess) {
return controller->paramLibAccessFailed(
status, indexName, axis->axisNo(), callerFunctionName, lineNumber);
}
return asynSuccess;
}
template <typename A, typename C>
asynStatus setAxisParamImpl(A *axis, C *controller, const char *indexName,
int (C::*func)(), char *writeValue,
const char *callerFunctionName, int lineNumber,
TypeTag<char *>) {
int indexValue = (controller->*func)();
asynStatus status = axis->setStringParam(indexValue, writeValue);
if (status != asynSuccess) {
return controller->paramLibAccessFailed(
status, indexName, axis->axisNo(), callerFunctionName, lineNumber);
}
return asynSuccess;
}
template <typename A, typename C>
asynStatus setAxisParamImpl(A *axis, C *controller, const char *indexName,
int (C::*func)(), const char *writeValue,
const char *callerFunctionName, int lineNumber,
TypeTag<const char *>) {
int indexValue = (controller->*func)();
asynStatus status = axis->setStringParam(indexValue, writeValue);
if (status != asynSuccess) {
return controller->paramLibAccessFailed(
status, indexName, axis->axisNo(), callerFunctionName, lineNumber);
}
return asynSuccess;
}
/**
* @brief Helper function to set an integer / double / string parameter for an
* axis in the paramLib
*
* This function should not be used directly, but rather through its macro
* variant `setAxisParamChecked`.
*
* @tparam A
* @tparam C
* @tparam T
* @param axis
* @param controller
* @param indexName
* @param func
* @param writeValue
* @param callerFunctionName
* @param lineNumber
* @return asynStatus
*/
template <typename A, typename C, typename T>
asynStatus setAxisParam(A *axis, C *controller, const char *indexName,
int (C::*func)(), T writeValue,
const char *callerFunctionName, int lineNumber) {
return setAxisParamImpl(axis, controller, indexName, func, writeValue,
callerFunctionName, lineNumber, TypeTag<T>{});
}
/**
* @brief Macro to set an paramLib parameter and error checking the return value
*
* This macro is a wrapper around `setIntegerParam` / `setDoubleParam` /
* `setStringParam` which checks if the operation was successfull. If it wasn't,
* it returns by calling the paramLibAccessFailed function.
*
* For example, the following input:
* ```
* setAxisParamChecked(this, motorStatusProblem_, false)
* ```
* expands into the following code:
* ```
* {
* int indexValue = controller->motorStatusProblem_();
* asynStatus status = axis->setIntegerParam(indexValue, writeValue);
* if (status != asynSuccess) {
* return controller->paramLibAccessFailed(
* status, "motorStatusProblem_", axis->axisNo(), __PRETTY_FUNCTION__,
* __LINE__);
* }
* return asynSuccess;
* }
* ```
* =============================================================================
*/
#define setAxisParamChecked(axis, indexSetterFunction, writeValue) \
do { \
auto *ctrlPtr = (axis)->pController(); \
using ControllerType = \
typename std::remove_pointer<decltype(ctrlPtr)>::type; \
asynStatus setStatus = \
setAxisParam(axis, ctrlPtr, #indexSetterFunction, \
static_cast<int (ControllerType::*)()>( \
&ControllerType::indexSetterFunction), \
writeValue, __PRETTY_FUNCTION__, __LINE__); \
if (setStatus != asynSuccess) \
return setStatus; \
} while (0)
// =============================================================================
// Helper functions and definitions for the macro getAxisParamChecked
// Generic fallback - if the compiler tries to compile this function, it fails.
template <typename A, typename C, typename T>
asynStatus getAxisParamImpl(A *axis, C *controller, const char *indexName,
int (C::*func)(), T *readValue,
const char *callerFunctionName, int lineNumber,
size_t msgSize, TypeTag<void>) {
static_assert(
sizeof(T) == 0,
"no specialization of getAxisParam exists for the given type");
return asynError;
}
template <typename A, typename C>
asynStatus getAxisParamImpl(A *axis, C *controller, const char *indexName,
int (C::*func)(), int *readValue,
const char *callerFunctionName, int lineNumber,
size_t /*msgSize*/, TypeTag<int>) {
int indexValue = (controller->*func)();
asynStatus status =
controller->getIntegerParam(axis->axisNo(), indexValue, readValue);
if (status != asynSuccess) {
return controller->paramLibAccessFailed(
status, indexName, axis->axisNo(), callerFunctionName, lineNumber);
}
return asynSuccess;
}
template <typename A, typename C>
asynStatus getAxisParamImpl(A *axis, C *controller, const char *indexName,
int (C::*func)(), bool *readValue,
const char *callerFunctionName, int lineNumber,
size_t msgSize, TypeTag<bool>) {
int readValueInt = 0;
asynStatus status = getAxisParamImpl(axis, controller, indexName, func,
&readValueInt, callerFunctionName,
lineNumber, msgSize, TypeTag<int>{});
*readValue = readValueInt != 0;
return status;
}
template <typename A, typename C>
asynStatus getAxisParamImpl(A *axis, C *controller, const char *indexName,
int (C::*func)(), double *readValue,
const char *callerFunctionName, int lineNumber,
size_t /*msgSize*/, TypeTag<double>) {
int indexValue = (controller->*func)();
asynStatus status =
controller->getDoubleParam(axis->axisNo(), indexValue, readValue);
if (status != asynSuccess) {
return controller->paramLibAccessFailed(
status, indexName, axis->axisNo(), callerFunctionName, lineNumber);
}
return asynSuccess;
}
template <typename A, typename C>
asynStatus getAxisParamImpl(A *axis, C *controller, const char *indexName,
int (C::*func)(), char *readValue,
const char *callerFunctionName, int lineNumber,
size_t msgSize, TypeTag<char>) {
int indexValue = (controller->*func)();
asynStatus status = controller->getStringParam(axis->axisNo(), indexValue,
msgSize, readValue);
if (status != asynSuccess) {
return controller->paramLibAccessFailed(
status, indexName, axis->axisNo(), callerFunctionName, lineNumber);
}
return asynSuccess;
}
template <typename A, typename C>
asynStatus getAxisParamImpl(A *axis, C *controller, const char *indexName,
int (C::*func)(), std::string *readValue,
const char *callerFunctionName, int lineNumber,
size_t /*msgSize*/, TypeTag<std::string>) {
int indexValue = (controller->*func)();
// Convert the pointer to a reference, since getStringParam expects the
// latter.
std::string &rReadValue = *readValue;
asynStatus status =
controller->getStringParam(axis->axisNo(), indexValue, rReadValue);
if (status != asynSuccess) {
return controller->paramLibAccessFailed(
status, indexName, axis->axisNo(), callerFunctionName, lineNumber);
}
return asynSuccess;
}
/**
* @brief Helper function to get an integer / double / string parameter for an
* axis in the paramLib
*
* This function should not be used directly, but rather through its macro
* variant `getAxisParamChecked`.
*
* @tparam T
* @param axis
* @param controller
* @param indexName
* @param func
* @param readValue
* @param callerFunctionName
* @param lineNumber
* @param maxChars Only used when readValue is a char*. Specifies the maximum
* number of characters which can be placed into the buffer the pointer points
* to.
* @return asynStatus
*/
template <typename A, typename C, typename T>
asynStatus getAxisParam(A *axis, C *controller, const char *indexName,
int (C::*func)(), T *readValue,
const char *callerFunctionName, int lineNumber) {
return getAxisParamImpl(axis, controller, indexName, func, readValue,
callerFunctionName, lineNumber,
controller->msgSize(), TypeTag<T>{});
}
/**
* @brief Helper function to get a string parameter for an
* axis in the paramLib into a char array
*
* This function should not be used directly, but rather through its macro
* variant `getAxisParamChecked`. It is a specialized variant of the general
* getAxisParam defined above for char arrays.
*
* @tparam A
* @tparam C
* @tparam N
* @param axis
* @param controller
* @param indexName
* @param func
* @param callerFunctionName
* @param lineNumber
* @return asynStatus
*/
template <typename A, typename C, size_t N>
asynStatus getAxisParam(A *axis, C *controller, const char *indexName,
int (C::*func)(), char (*readValue)[N],
const char *callerFunctionName, int lineNumber) {
return getAxisParamImpl(axis, controller, indexName, func, *readValue,
callerFunctionName, lineNumber, N, TypeTag<char>{});
}
/**
* @brief Macro to get an paramLib parameter and error checking the return value
*
* This macro is a wrapper around `getIntegerParam` / `getDoubleParam` /
* `getStringParam` which checks if the operation was successfull. If it wasn't,
* it returns by calling the paramLibAccessFailed function. In order
*
* For example, the following input:
* ```
* getAxisParamChecked(this, motorStatusProblem_, &readValue)
* ```
* expands into the following code:
* ```
* {
* int indexValue = controller->motorStatusProblem_();
* asynStatus status = controller->getIntegerParam(axis->axisNo(),
* indexValue, readValue); if (status != asynSuccess) { return
* controller->paramLibAccessFailed( status, "motorStatusProblem_",
* axis->axisNo(), __PRETTY_FUNCTION__,
* __LINE__);
* }
* return asynSuccess;
* }
* ```
* =============================================================================
*/
#define getAxisParamChecked(axis, indexGetterFunction, readValue) \
do { \
auto *ctrlPtr = (axis)->pController(); \
using ControllerType = \
typename std::remove_pointer<decltype(ctrlPtr)>::type; \
asynStatus getStatus = \
getAxisParam(axis, ctrlPtr, #indexGetterFunction, \
static_cast<int (ControllerType::*)()>( \
&ControllerType::indexGetterFunction), \
readValue, __PRETTY_FUNCTION__, __LINE__); \
if (getStatus != asynSuccess) \
return getStatus; \
} while (0)
#endif