Files
sinqmotor/src/sinqController.h
2025-04-25 13:17:41 +02:00

377 lines
16 KiB
C++

/*
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
#include "asynMotorController.h"
#include "msgPrintControl.h"
#include <deque>
#include <initHooks.h>
#include <unordered_map>
#define motorMessageIsFromDriverString "MOTOR_MESSAGE_DRIVER"
#define motorMessageTextString "MOTOR_MESSAGE_TEXT"
#define IncrementalEncoder "incremental"
#define AbsoluteEncoder "absolute"
#define NoEncoder "none"
class epicsShareClass 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 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) {
comTimeoutWindow_ = comTimeoutWindow;
maxNumberTimeouts_ = maxNumberTimeouts;
return asynSuccess;
}
/**
* @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) {
maxSubsequentTimeouts_ = maxSubsequentTimeouts;
return asynSuccess;
}
/**
* @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();
// =========================================================================
// 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
int motorMessageText() { return motorMessageText_; }
int motorReset() { return motorReset_; }
int motorEnable() { return motorEnable_; }
int motorEnableRBV() { return motorEnableRBV_; }
int motorCanDisable() { return motorCanDisable_; }
int motorEnableMovWatchdog() { return motorEnableMovWatchdog_; }
int motorCanSetSpeed() { return motorCanSetSpeed_; }
int motorLimitsOffset() { return motorLimitsOffset_; }
int motorForceStop() { return motorForceStop_; }
int motorConnected() { return motorConnected_; }
int motorVeloFromDriver() { return motorVeloFromDriver_; }
int motorVbasFromDriver() { return motorVbasFromDriver_; }
int motorVmaxFromDriver() { return motorVmaxFromDriver_; }
int motorAcclFromDriver() { return motorAcclFromDriver_; }
int motorHighLimitFromDriver() { return motorHighLimitFromDriver_; }
int motorLowLimitFromDriver() { return motorLowLimitFromDriver_; }
int adaptivePolling() { return adaptivePolling_; }
int encoderType() { return 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() { return pasynOctetSyncIOipPort_; }
// =========================================================================
protected:
// Pointer to the port user which is specified by the char array
// `ipPortConfigName` in the constructor
asynUser *pasynOctetSyncIOipPort_;
double movingPollPeriod_;
double idlePollPeriod_;
msgPrintControl msgPrintControl_;
// Internal variables used in the communication timeout frequency watchdog
time_t comTimeoutWindow_; // Size of the time window
size_t maxNumberTimeouts_; // Maximum acceptable number of events within the
// time window
std::deque<time_t>
timeoutEvents_; // Deque holding the timestamps of the individual events
// Communicate a timeout to the user after it has happened this many times
// in a row
int maxSubsequentTimeouts_;
bool maxSubsequentTimeoutsExceeded_;
/*
See the documentation in db/sinqMotor.db for the following integers
*/
#define FIRST_SINQMOTOR_PARAM motorMessageText_
int motorMessageText_;
int motorReset_;
int motorEnable_;
int motorEnableRBV_;
int motorCanDisable_;
int motorEnableMovWatchdog_;
int motorCanSetSpeed_;
int motorLimitsOffset_;
int motorForceStop_;
int motorConnected_;
/*
These parameters are here to write values from the hardware to the EPICS
motor record. Using motorHighLimit_ / motorLowLimit_ does not work:
https://epics.anl.gov/tech-talk/2023/msg00576.php. Therefore, some
additional records are introduced which read from these parameters and write
into the motor record.
*/
int motorVeloFromDriver_;
int motorVbasFromDriver_;
int motorVmaxFromDriver_;
int motorAcclFromDriver_;
int motorHighLimitFromDriver_;
int motorLowLimitFromDriver_;
int adaptivePolling_;
int encoderType_;
#define LAST_SINQMOTOR_PARAM encoderType_
private:
static void epicsInithookFunction(initHookState iState);
};
#define NUM_SINQMOTOR_DRIVER_PARAMS \
(&LAST_SINQMOTOR_PARAM - &FIRST_SINQMOTOR_PARAM + 1)
#endif