356 lines
15 KiB
C++
356 lines
15 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 in sync with the electronics counting logic, 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 *SINQPortName, 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 motor message text 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 errMsgCouldNotParseResponse(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 sztatus 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 motorVeloFromDriver() { return motorVeloFromDriver_; }
|
|
int motorVbasFromDriver() { return motorVbasFromDriver_; }
|
|
int motorVmaxFromDriver() { return motorVmaxFromDriver_; }
|
|
int motorAcclFromDriver() { return motorAcclFromDriver_; }
|
|
int motorHighLimitFromDriver() { return motorHighLimitFromDriver_; }
|
|
int motorLowLimitFromDriver() { return motorLowLimitFromDriver_; }
|
|
int encoderType() { return encoderType_; }
|
|
|
|
// Additional members
|
|
int numAxes() { return numAxes_; }
|
|
asynUser *asynUserSelf() { return pasynUserSelf; }
|
|
asynUser *ipPortUser() { return ipPortUser_; }
|
|
|
|
// =========================================================================
|
|
|
|
protected:
|
|
// Pointer to the port user which is specified by the char array
|
|
// ipPortConfigName in the constructor
|
|
asynUser *ipPortUser_;
|
|
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_;
|
|
|
|
#define FIRST_SINQMOTOR_PARAM motorMessageText_
|
|
int motorMessageText_;
|
|
int motorReset_;
|
|
int motorEnable_;
|
|
int motorEnableRBV_;
|
|
int motorCanDisable_;
|
|
int motorEnableMovWatchdog_;
|
|
int motorCanSetSpeed_;
|
|
int motorLimitsOffset_;
|
|
int motorForceStop_;
|
|
/*
|
|
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 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
|