Files
sinqMotor/src/sinqController.h
smathis 902b18d038
All checks were successful
Test And Build / Lint (push) Successful in 6s
Test And Build / Build (push) Successful in 7s
Excempt EPICS libraries from -Weffc++
2025-09-17 12:18:06 +02:00

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