421 lines
16 KiB
C++
421 lines
16 KiB
C++
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
/*
|
|
This class extends asynMotorController by some features used in SINQ. See the
|
|
README.md for details.
|
|
|
|
Stefan Mathis, November 2024
|
|
*/
|
|
|
|
#ifndef sinqController_H
|
|
#define sinqController_H
|
|
|
|
// The EPICS libaries do not follow -Weffc++
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Weffc++"
|
|
|
|
#include "asynMotorController.h"
|
|
#include "msgPrintControl.h"
|
|
#include <initHooks.h>
|
|
#include <macros.h>
|
|
|
|
#pragma GCC diagnostic pop
|
|
|
|
#include <memory>
|
|
|
|
#define motorMessageIsFromDriverString "MOTOR_MESSAGE_DRIVER"
|
|
#define motorMessageTextString "MOTOR_MESSAGE_TEXT"
|
|
#define IncrementalEncoder "incremental"
|
|
#define AbsoluteEncoder "absolute"
|
|
#define NoEncoder "none"
|
|
|
|
struct HIDDEN sinqControllerImpl;
|
|
|
|
class HIDDEN sinqController : public asynMotorController {
|
|
public:
|
|
/**
|
|
* @brief Construct a new sinqController object
|
|
*
|
|
* @param portName Controller can be found by findAsynPortDriver
|
|
* with this name
|
|
* @param ipPortConfigName IP adress and port configuration of the
|
|
* controller unit, used to connect via pasynOctetSyncIO->connect
|
|
* @param numAxes Pointers to the axes are stored in the array
|
|
pAxes_ which has the length specified here. When getting an axis, the
|
|
`getAxis` function indexes into this array. A length of 8 would therefore
|
|
mean that the axis slots 0 to 7 are available. However, in order to keep the
|
|
axis enumeration identical to that of the hardware, we start counting the
|
|
axes with 1 and end at 8. Therefore, an offset of 1 is added when forwarding
|
|
this number to asynMotorController.
|
|
* @param movingPollPeriod Time between polls when moving (in seconds)
|
|
* @param idlePollPeriod Time between polls when not moving (in
|
|
seconds)
|
|
* @param extraParams Number of extra parameter library entries
|
|
* created in a concrete driver implementation
|
|
*/
|
|
sinqController(const char *portName, const char *ipPortConfigName,
|
|
int numAxes, double movingPollPeriod, double idlePollPeriod,
|
|
int numExtraParams);
|
|
|
|
/**
|
|
* @brief Destroy the sinqController object
|
|
*
|
|
* In general, there is no real memory cleanup strategy in asynMotor,
|
|
* because objects are expected to be alive for the entire lifetime of the
|
|
* IOC. We just clean up the allocated axes array here.
|
|
*/
|
|
virtual ~sinqController(void);
|
|
|
|
/**
|
|
* @brief Delete the copy and copy assignment constructors, because this
|
|
* class should not be copied (it is tied to hardware!)
|
|
*/
|
|
sinqController(const sinqController &) = delete;
|
|
sinqController &operator=(const sinqController &) = delete;
|
|
|
|
/**
|
|
* @brief Overloaded function of asynMotorController
|
|
*
|
|
* The function is overloaded to allow enabling / disabling the motor.
|
|
*
|
|
* @param pasynUser Specify the axis via the asynUser
|
|
* @param value New value
|
|
* @return asynStatus
|
|
*/
|
|
virtual asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value);
|
|
|
|
/**
|
|
* @brief Overloaded function of asynMotorController
|
|
*
|
|
* The function is overloaded to get readback values for the enabling /
|
|
* disabling status.
|
|
*
|
|
* @param pasynUser Specify the axis via the asynUser
|
|
* @param value Read-out value
|
|
* @return asynStatus
|
|
*/
|
|
asynStatus readInt32(asynUser *pasynUser, epicsInt32 *value);
|
|
|
|
/**
|
|
* @brief Error handling in case accessing the parameter library failed.
|
|
*
|
|
* If accessing the parameter library failed (return status !=
|
|
asynSuccess), calling this function writes a standardized message to both
|
|
the IOC shell and the motorMessageText PV. It then returns the input
|
|
status.
|
|
*
|
|
* @param status Status of the failed parameter library access
|
|
* @param parameter Name of the parameter, used to create the
|
|
error messages.
|
|
* @param functionName Name of the caller function. It is recommended
|
|
to use a macro, e.g. __func__ or __PRETTY_FUNCTION__.
|
|
* @param line Source code line where this function is
|
|
called. It is recommended to use a macro, e.g. __LINE__.
|
|
* @return asynStatus Returns input status.
|
|
*/
|
|
asynStatus paramLibAccessFailed(asynStatus status, const char *parameter,
|
|
int axisNo, const char *functionName,
|
|
int line);
|
|
|
|
/**
|
|
* @brief Error handling in case parsing a command response failed.
|
|
*
|
|
* This function writes a standardized message to both the IOC shell and
|
|
the motor message text PV in case parsing a response (e.g. via sscanf)
|
|
failed. It always returns asynError. This is convenience feature so the
|
|
function call can be used as a return value in an error handling branch.
|
|
*
|
|
* @param command Command which led to the unparseable message
|
|
* @param response Response which wasn't parseable
|
|
* @param axisNo_ Axis where the problem occurred
|
|
* @param functionName Name of the caller function. It is recommended
|
|
to use a macro, e.g. __func__ or __PRETTY_FUNCTION__.
|
|
* @param line Source code line where this function is
|
|
called. It is recommended to use a macro, e.g. __LINE__.
|
|
* @return asynStatus Returns asynError.
|
|
*/
|
|
asynStatus couldNotParseResponse(const char *command, const char *response,
|
|
int axisNo, const char *functionName,
|
|
int line);
|
|
|
|
/**
|
|
* @brief Convert an asynStatus into a descriptive string.
|
|
*
|
|
* @param status Status which should be converted to a string.
|
|
* @return const char*
|
|
*/
|
|
const char *stringifyAsynStatus(asynStatus status);
|
|
|
|
/**
|
|
* @brief This function should be called when a communication timeout
|
|
occured. It calculates the frequency of communication timeout events and
|
|
creates an error message, if an threshold has been exceeded.
|
|
|
|
Occasionally, communication timeouts between the IOC and the motor
|
|
controller may happen, usually because the controller takes too long to
|
|
respond. If this happens infrequently, this is not a problem. However, if it
|
|
happens very often, this may indicate a network problem and must therefore
|
|
be forwarded to the user. This is checked by calculating the moving average
|
|
of events and comparing it to a threshhold. Both the threshold and the time
|
|
window for the moving average can be configured in the IOC via the function
|
|
setThresholdCom.
|
|
|
|
This function exists in two variants: Either the error message can be
|
|
written into a buffer provided by the caller or it written directly into the
|
|
parameter library of the provided axis.
|
|
|
|
* @param axis Axis to which the error message is sent
|
|
*
|
|
* @return asynStatus asynError, if the threshold has been
|
|
exceeded, asynSuccess otherwise
|
|
*/
|
|
virtual asynStatus checkComTimeoutWatchdog(class sinqAxis *axis);
|
|
|
|
/**
|
|
* @brief See documentation of checkComTimeoutWatchdog(sinqAxis * axis)
|
|
*
|
|
* @param userMessage Buffer for the user message
|
|
* @param userMessageSize Buffer size in chars
|
|
* @return asynStatus
|
|
*/
|
|
virtual asynStatus checkComTimeoutWatchdog(int axisNo, char *motorMessage,
|
|
size_t motorMessageSize);
|
|
|
|
/**
|
|
* @brief Set the threshold for the communication timeout mechanism
|
|
*
|
|
* @param comTimeoutWindow Size of the time window used to calculate
|
|
* the moving average of timeout events. Set this value to 0 to deactivate
|
|
* the watchdog.
|
|
* @param maxNumberTimeouts Maximum number of timeouts which may occur
|
|
* within the time window before the watchdog is triggered.
|
|
* @return asynStatus
|
|
*/
|
|
virtual asynStatus setThresholdComTimeout(time_t comTimeoutWindow,
|
|
size_t maxNumberTimeouts);
|
|
|
|
/**
|
|
* @brief Inform the user, if the number of timeouts exceeds the threshold
|
|
* specified with `setMaxSubsequentTimeouts`.
|
|
*
|
|
* @param timeoutNo Number of subsequent timeouts which already
|
|
* happened.
|
|
* @param axis
|
|
* @return asynStatus
|
|
*/
|
|
virtual asynStatus checkMaxSubsequentTimeouts(int timeoutNo,
|
|
class sinqAxis *axis);
|
|
|
|
/**
|
|
* @brief See documentation of `checkMaxSubsequentTimeouts(sinqAxis * axis)`
|
|
*
|
|
* @param userMessage Buffer for the user message
|
|
* @param userMessageSize Buffer size in chars
|
|
* @return asynStatus
|
|
*/
|
|
virtual asynStatus checkMaxSubsequentTimeouts(int timeoutNo, int axisNo,
|
|
char *motorMessage,
|
|
size_t motorMessageSize);
|
|
|
|
/**
|
|
* @brief Set the maximum number of subsequent timeouts before the user is
|
|
* informed.
|
|
*
|
|
* @param maxSubsequentTimeouts
|
|
* @return asynStatus
|
|
*/
|
|
asynStatus setMaxSubsequentTimeouts(int maxSubsequentTimeouts);
|
|
|
|
/**
|
|
* @brief If true, the maximum number of subsequent communication timeouts
|
|
* set in `setMaxSubsequentTimeouts` has been exceeded
|
|
*
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
bool maxSubsequentTimeoutsExceeded();
|
|
|
|
/**
|
|
* @brief Get a reference to the map used to control the maximum number of
|
|
* message repetitions. See the documentation of `printRepetitionWatchdog`
|
|
* in msgPrintControl.h for details.
|
|
*/
|
|
msgPrintControl &getMsgPrintControl();
|
|
|
|
/**
|
|
* @brief Get the axis object
|
|
*
|
|
* @param pasynUser Specify the axis via the asynUser
|
|
* @return sinqAxis* If no axis could be found, this is a nullptr
|
|
*/
|
|
sinqAxis *getSinqAxis(asynUser *pasynUser);
|
|
|
|
/**
|
|
* @brief Get the axis object
|
|
*
|
|
* @param axisNo Specify the axis via its index
|
|
* @return sinqAxis* If no axis could be found, this is a nullptr
|
|
*/
|
|
sinqAxis *getSinqAxis(int axisNo);
|
|
|
|
// =========================================================================
|
|
// Public getters for protected members
|
|
|
|
// Accessors for double parameters
|
|
int motorMoveRel() { return motorMoveRel_; }
|
|
int motorMoveAbs() { return motorMoveAbs_; }
|
|
int motorMoveVel() { return motorMoveVel_; }
|
|
int motorHome() { return motorHome_; }
|
|
int motorStop() { return motorStop_; }
|
|
int motorVelocity() { return motorVelocity_; }
|
|
int motorVelBase() { return motorVelBase_; }
|
|
int motorAccel() { return motorAccel_; }
|
|
int motorPosition() { return motorPosition_; }
|
|
int motorEncoderPosition() { return motorEncoderPosition_; }
|
|
int motorDeferMoves() { return motorDeferMoves_; }
|
|
int motorMoveToHome() { return motorMoveToHome_; }
|
|
int motorResolution() { return motorResolution_; }
|
|
int motorEncoderRatio() { return motorEncoderRatio_; }
|
|
int motorPGain() { return motorPGain_; }
|
|
int motorIGain() { return motorIGain_; }
|
|
int motorDGain() { return motorDGain_; }
|
|
int motorHighLimit() { return motorHighLimit_; }
|
|
int motorLowLimit() { return motorLowLimit_; }
|
|
int motorClosedLoop() { return motorClosedLoop_; }
|
|
int motorPowerAutoOnOff() { return motorPowerAutoOnOff_; }
|
|
int motorPowerOnDelay() { return motorPowerOnDelay_; }
|
|
int motorPowerOffDelay() { return motorPowerOffDelay_; }
|
|
int motorPowerOffFraction() { return motorPowerOffFraction_; }
|
|
int motorPostMoveDelay() { return motorPostMoveDelay_; }
|
|
int motorStatus() { return motorStatus_; }
|
|
int motorUpdateStatus() { return motorUpdateStatus_; }
|
|
|
|
// Accessors for status bits (integers)
|
|
int motorStatusDirection() { return motorStatusDirection_; }
|
|
int motorStatusDone() { return motorStatusDone_; }
|
|
int motorStatusHighLimit() { return motorStatusHighLimit_; }
|
|
int motorStatusAtHome() { return motorStatusAtHome_; }
|
|
int motorStatusSlip() { return motorStatusSlip_; }
|
|
int motorStatusPowerOn() { return motorStatusPowerOn_; }
|
|
int motorStatusFollowingError() { return motorStatusFollowingError_; }
|
|
int motorStatusHome() { return motorStatusHome_; }
|
|
int motorStatusHasEncoder() { return motorStatusHasEncoder_; }
|
|
int motorStatusProblem() { return motorStatusProblem_; }
|
|
int motorStatusMoving() { return motorStatusMoving_; }
|
|
int motorStatusGainSupport() { return motorStatusGainSupport_; }
|
|
int motorStatusCommsError() { return motorStatusCommsError_; }
|
|
int motorStatusLowLimit() { return motorStatusLowLimit_; }
|
|
int motorStatusHomed() { return motorStatusHomed_; }
|
|
|
|
// Parameters for passing additional motor record information to the driver
|
|
int motorRecResolution() { return motorRecResolution_; }
|
|
int motorRecDirection() { return motorRecDirection_; }
|
|
int motorRecOffset() { return motorRecOffset_; }
|
|
|
|
// Accessors for additional PVs defined in sinqController (which are hidden
|
|
// in pSinqC_)
|
|
int motorMessageText();
|
|
int motorReset();
|
|
int motorEnable();
|
|
int motorEnableRBV();
|
|
int motorCanDisable();
|
|
int motorEnableMovWatchdog();
|
|
int motorCanSetSpeed();
|
|
int motorLimitsOffset();
|
|
int motorForceStop();
|
|
int motorConnected();
|
|
int motorVeloFromDriver();
|
|
int motorVbasFromDriver();
|
|
int motorVmaxFromDriver();
|
|
int motorAcclFromDriver();
|
|
int motorHighLimitFromDriver();
|
|
int motorLowLimitFromDriver();
|
|
int motorPositionDeadband();
|
|
int adaptivePolling();
|
|
int encoderType();
|
|
|
|
// Additional members
|
|
int numAxes() { return numAxes_; }
|
|
double idlePollPeriod() { return idlePollPeriod_; }
|
|
double movingPollPeriod() { return movingPollPeriod_; }
|
|
|
|
/**
|
|
* @brief Return a pointer to the asynUser of the controller
|
|
*
|
|
* @return asynUser*
|
|
*/
|
|
asynUser *pasynUser() { return pasynUserSelf; }
|
|
|
|
/**
|
|
* @brief Return a pointer to the low-level octet (string) IP Port
|
|
*
|
|
* @return asynUser*
|
|
*/
|
|
asynUser *pasynOctetSyncIOipPort();
|
|
|
|
/**
|
|
* @brief Overloaded version of `asynController::poll` which decreases
|
|
* `outstandingForcedFastPolls` and then defers to the base method
|
|
*/
|
|
asynStatus poll();
|
|
|
|
/**
|
|
* @brief Overloaded version of `asynController::wakeupPoller` which
|
|
* initializes the `outstandingForcedFastPolls` variable and then defers to
|
|
* the base class method.
|
|
*
|
|
* The `wakePoller` function of the base class `asynController` sends a
|
|
* signal to the poller thread which forces the latter to perform a number
|
|
* of fast / busy polls with the busy poll period regardless of whether the
|
|
* motor is moving or not. The number of polls is specified by
|
|
* "forcedFastPolls()" and can be set with `setForcedFastPolls()`.
|
|
*
|
|
* @return asynStatus
|
|
*/
|
|
asynStatus wakeupPoller();
|
|
|
|
/**
|
|
* @brief Set the number of forced fast polls which should be performed when
|
|
* `wakeupPoller` is called.
|
|
*
|
|
* @param forcedFastPolls
|
|
*/
|
|
void setForcedFastPolls(int forcedFastPolls) {
|
|
forcedFastPolls_ = forcedFastPolls;
|
|
}
|
|
|
|
/**
|
|
* @brief Read the number of forced fast polls currently specified
|
|
*
|
|
*/
|
|
int forcedFastPolls() { return forcedFastPolls_; }
|
|
|
|
/**
|
|
* @brief Read the number of outstanding forced fast polls currently
|
|
* specified
|
|
*
|
|
*/
|
|
int outstandingForcedFastPolls();
|
|
|
|
/**
|
|
* @brief Return the maximum error message buffer size
|
|
*
|
|
* This is an empirical value which must be large enough to avoid overflows
|
|
* for all commands to the device / responses from it.
|
|
*
|
|
* @return uint32_t
|
|
*/
|
|
uint32_t msgSize() { return MAXBUF_; }
|
|
|
|
// Maximum message size
|
|
static const uint32_t MAXBUF_ = 200;
|
|
|
|
// =========================================================================
|
|
|
|
private:
|
|
std::unique_ptr<sinqControllerImpl> pSinqC_;
|
|
static void epicsInithookFunction(initHookState iState);
|
|
};
|
|
|
|
#endif
|