722 lines
28 KiB
C++
722 lines
28 KiB
C++
#include "sinqController.h"
|
|
#include "asynMotorController.h"
|
|
#include "asynOctetSyncIO.h"
|
|
#include "epicsExport.h"
|
|
#include "iocsh.h"
|
|
#include "sinqAxis.h"
|
|
#include <errlog.h>
|
|
#include <vector>
|
|
|
|
/*
|
|
Contains all instances of turboPmacAxis which have been created and is used in
|
|
the initialization hook function.
|
|
*/
|
|
static std::vector<sinqController *> controller;
|
|
|
|
/**
|
|
* @brief Hook function to perform certain actions during the IOC initialization
|
|
*
|
|
* @param iState
|
|
*/
|
|
void sinqController::epicsInithookFunction(initHookState iState) {
|
|
if (iState == initHookAfterIocRunning) {
|
|
// Iterate through all axes of each and call the initialization method
|
|
// on each one of them.
|
|
for (std::vector<sinqController *>::iterator itC = controller.begin();
|
|
itC != controller.end(); ++itC) {
|
|
|
|
sinqController *controller = *itC;
|
|
controller->startPoller(controller->movingPollPeriod_,
|
|
controller->idlePollPeriod_, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
sinqController::sinqController(const char *portName,
|
|
const char *ipPortConfigName, int numAxes,
|
|
double movingPollPeriod, double idlePollPeriod,
|
|
int numExtraParams)
|
|
: asynMotorController(
|
|
portName,
|
|
// As described in the function documentation, an offset of 1 is
|
|
// added for better readability of the configuration.
|
|
numAxes + 1,
|
|
NUM_MOTOR_DRIVER_PARAMS + NUM_SINQMOTOR_DRIVER_PARAMS +
|
|
numExtraParams,
|
|
0, // No additional interfaces beyond those in base class
|
|
0, // No additional callback interfaces beyond those in base class
|
|
ASYN_CANBLOCK | ASYN_MULTIDEVICE,
|
|
1, // autoconnect
|
|
0, 0), // Default priority and stack size
|
|
msgPrintControl_(4) {
|
|
|
|
asynStatus status = asynSuccess;
|
|
|
|
// Handle to the asynUser of the IP port asyn driver
|
|
pasynOctetSyncIOipPort_ = nullptr;
|
|
|
|
// Initial values for the average timeout mechanism, can be overwritten
|
|
// later by a FFI function
|
|
comTimeoutWindow_ = 3600; // seconds
|
|
|
|
// Number of timeouts which may occur before an error is forwarded to the
|
|
// user
|
|
maxNumberTimeouts_ = 60;
|
|
|
|
// Queue holding the timeout event timestamps
|
|
timeoutEvents_ = {};
|
|
|
|
// Inform the user after 10 timeouts in a row (default value)
|
|
maxSubsequentTimeouts_ = 10;
|
|
maxSubsequentTimeoutsExceeded_ = false;
|
|
|
|
// Store the poll period information. The poller itself will be started
|
|
// later (after the IOC is running in epicsInithookFunction)
|
|
movingPollPeriod_ = movingPollPeriod;
|
|
idlePollPeriod_ = idlePollPeriod;
|
|
|
|
// =========================================================================;
|
|
|
|
/*
|
|
We try to connect to the port via the port name provided by the constructor.
|
|
If this fails, the function is terminated via exit.
|
|
*/
|
|
pasynOctetSyncIO->connect(ipPortConfigName, 0, &pasynOctetSyncIOipPort_,
|
|
NULL);
|
|
if (status != asynSuccess || pasynOctetSyncIOipPort_ == nullptr) {
|
|
errlogPrintf("Controller \"%s\" => %s, line %d:\nFATAL ERROR (cannot "
|
|
"connect to MCU controller).\n"
|
|
"Terminating IOC",
|
|
portName, __PRETTY_FUNCTION__, __LINE__);
|
|
exit(-1);
|
|
}
|
|
|
|
// =========================================================================;
|
|
|
|
// MOTOR_MESSAGE_TEXT corresponds to the PV definition inside sinqMotor.db.
|
|
// This text is used to forward status messages to NICOS and in turn to the
|
|
// user.
|
|
status =
|
|
createParam("MOTOR_MESSAGE_TEXT", asynParamOctet, &motorMessageText_);
|
|
if (status != asynSuccess) {
|
|
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
|
|
"parameter failed with %s).\nTerminating IOC",
|
|
portName, __PRETTY_FUNCTION__, __LINE__,
|
|
stringifyAsynStatus(status));
|
|
exit(-1);
|
|
}
|
|
|
|
status = createParam("MOTOR_ENABLE", asynParamInt32, &motorEnable_);
|
|
if (status != asynSuccess) {
|
|
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
|
|
"parameter failed with %s).\nTerminating IOC",
|
|
portName, __PRETTY_FUNCTION__, __LINE__,
|
|
stringifyAsynStatus(status));
|
|
exit(-1);
|
|
}
|
|
|
|
status = createParam("MOTOR_RESET", asynParamInt32, &motorReset_);
|
|
if (status != asynSuccess) {
|
|
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
|
|
"parameter failed with %s).\nTerminating IOC",
|
|
portName, __PRETTY_FUNCTION__, __LINE__,
|
|
stringifyAsynStatus(status));
|
|
exit(-1);
|
|
}
|
|
|
|
status = createParam("MOTOR_ENABLE_RBV", asynParamInt32, &motorEnableRBV_);
|
|
if (status != asynSuccess) {
|
|
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
|
|
"parameter failed with %s).\nTerminating IOC",
|
|
portName, __PRETTY_FUNCTION__, __LINE__,
|
|
stringifyAsynStatus(status));
|
|
exit(-1);
|
|
}
|
|
|
|
status =
|
|
createParam("MOTOR_CAN_DISABLE", asynParamInt32, &motorCanDisable_);
|
|
if (status != asynSuccess) {
|
|
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
|
|
"parameter failed with %s).\nTerminating IOC",
|
|
portName, __PRETTY_FUNCTION__, __LINE__,
|
|
stringifyAsynStatus(status));
|
|
exit(-1);
|
|
}
|
|
|
|
status =
|
|
createParam("MOTOR_CAN_SET_SPEED", asynParamInt32, &motorCanSetSpeed_);
|
|
if (status != asynSuccess) {
|
|
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
|
|
"parameter failed with %s).\nTerminating IOC",
|
|
portName, __PRETTY_FUNCTION__, __LINE__,
|
|
stringifyAsynStatus(status));
|
|
exit(-1);
|
|
}
|
|
|
|
status = createParam("MOTOR_LIMITS_OFFSET", asynParamFloat64,
|
|
&motorLimitsOffset_);
|
|
if (status != asynSuccess) {
|
|
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
|
|
"parameter failed with %s).\nTerminating IOC",
|
|
portName, __PRETTY_FUNCTION__, __LINE__,
|
|
stringifyAsynStatus(status));
|
|
exit(-1);
|
|
}
|
|
|
|
/*
|
|
We need to introduce 2 new parameters in order to write the limits from the
|
|
driver to the EPICS record. See the comment in sinqController.h next to
|
|
the declaration of motorHighLimitFromDriver_.
|
|
*/
|
|
status = createParam("MOTOR_HIGH_LIMIT_FROM_DRIVER", asynParamFloat64,
|
|
&motorHighLimitFromDriver_);
|
|
if (status != asynSuccess) {
|
|
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
|
|
"parameter failed with %s).\nTerminating IOC",
|
|
portName, __PRETTY_FUNCTION__, __LINE__,
|
|
stringifyAsynStatus(status));
|
|
exit(-1);
|
|
}
|
|
|
|
status = createParam("MOTOR_LOW_LIMIT_FROM_DRIVER", asynParamFloat64,
|
|
&motorLowLimitFromDriver_);
|
|
if (status != asynSuccess) {
|
|
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
|
|
"parameter failed with %s).\nTerminating IOC",
|
|
portName, __PRETTY_FUNCTION__, __LINE__,
|
|
stringifyAsynStatus(status));
|
|
exit(-1);
|
|
}
|
|
|
|
status = createParam("MOTOR_ENABLE_MOV_WATCHDOG", asynParamInt32,
|
|
&motorEnableMovWatchdog_);
|
|
if (status != asynSuccess) {
|
|
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
|
|
"parameter failed with %s).\nTerminating IOC",
|
|
portName, __PRETTY_FUNCTION__, __LINE__,
|
|
stringifyAsynStatus(status));
|
|
exit(-1);
|
|
}
|
|
|
|
status = createParam("MOTOR_VELO_FROM_DRIVER", asynParamFloat64,
|
|
&motorVeloFromDriver_);
|
|
if (status != asynSuccess) {
|
|
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
|
|
"parameter failed with %s).\nTerminating IOC",
|
|
portName, __PRETTY_FUNCTION__, __LINE__,
|
|
stringifyAsynStatus(status));
|
|
exit(-1);
|
|
}
|
|
|
|
status = createParam("MOTOR_VBAS_FROM_DRIVER", asynParamFloat64,
|
|
&motorVbasFromDriver_);
|
|
if (status != asynSuccess) {
|
|
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
|
|
"parameter failed with %s).\nTerminating IOC",
|
|
portName, __PRETTY_FUNCTION__, __LINE__,
|
|
stringifyAsynStatus(status));
|
|
exit(-1);
|
|
}
|
|
|
|
status = createParam("MOTOR_VMAX_FROM_DRIVER", asynParamFloat64,
|
|
&motorVmaxFromDriver_);
|
|
if (status != asynSuccess) {
|
|
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
|
|
"parameter failed with %s).\nTerminating IOC",
|
|
portName, __PRETTY_FUNCTION__, __LINE__,
|
|
stringifyAsynStatus(status));
|
|
exit(-1);
|
|
}
|
|
|
|
status = createParam("MOTOR_ACCL_FROM_DRIVER", asynParamFloat64,
|
|
&motorAcclFromDriver_);
|
|
if (status != asynSuccess) {
|
|
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
|
|
"parameter failed with %s).\nTerminating IOC",
|
|
portName, __PRETTY_FUNCTION__, __LINE__,
|
|
stringifyAsynStatus(status));
|
|
exit(-1);
|
|
}
|
|
|
|
status = createParam("ADAPTIVE_POLLING", asynParamInt32, &adaptivePolling_);
|
|
if (status != asynSuccess) {
|
|
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
|
|
"parameter failed with %s).\nTerminating IOC",
|
|
portName, __PRETTY_FUNCTION__, __LINE__,
|
|
stringifyAsynStatus(status));
|
|
exit(-1);
|
|
}
|
|
|
|
status = createParam("ENCODER_TYPE", asynParamOctet, &encoderType_);
|
|
if (status != asynSuccess) {
|
|
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
|
|
"parameter failed with %s).\nTerminating IOC",
|
|
portName, __PRETTY_FUNCTION__, __LINE__,
|
|
stringifyAsynStatus(status));
|
|
exit(-1);
|
|
}
|
|
|
|
status = createParam("MOTOR_FORCE_STOP", asynParamInt32, &motorForceStop_);
|
|
if (status != asynSuccess) {
|
|
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
|
|
"parameter failed with %s).\nTerminating IOC",
|
|
portName, __PRETTY_FUNCTION__, __LINE__,
|
|
stringifyAsynStatus(status));
|
|
exit(-1);
|
|
}
|
|
|
|
// Register the hook function during construction of the first axis object
|
|
if (controller.empty()) {
|
|
initHookRegister(&epicsInithookFunction);
|
|
}
|
|
|
|
// Collect all axes into this list which will be used in the hook function
|
|
controller.push_back(this);
|
|
}
|
|
|
|
sinqController::~sinqController(void) {
|
|
|
|
// Free all axes
|
|
for (int axisNo = 0; axisNo < numAxes_; axisNo++) {
|
|
if (pAxes_[axisNo] != nullptr) {
|
|
delete pAxes_[axisNo];
|
|
}
|
|
}
|
|
|
|
// Cleanup of the array allocated in the asynMotorController constructor
|
|
free(this->pAxes_);
|
|
}
|
|
|
|
msgPrintControl &sinqController::getMsgPrintControl() {
|
|
return msgPrintControl_;
|
|
}
|
|
|
|
asynStatus sinqController::writeInt32(asynUser *pasynUser, epicsInt32 value) {
|
|
int function = pasynUser->reason;
|
|
|
|
// =====================================================================
|
|
|
|
asynMotorAxis *asynAxis = getAxis(pasynUser);
|
|
sinqAxis *axis = dynamic_cast<sinqAxis *>(asynAxis);
|
|
|
|
if (axis == nullptr) {
|
|
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\", axis %d => %s, line %d:\nAxis is not an "
|
|
"instance of sinqAxis",
|
|
portName, axis->axisNo(), __PRETTY_FUNCTION__, __LINE__);
|
|
return asynError;
|
|
}
|
|
|
|
// Handle custom PVs
|
|
if (function == motorEnable_) {
|
|
return axis->enable(value != 0);
|
|
} else if (function == motorReset_) {
|
|
return axis->reset();
|
|
} else if (function == motorForceStop_) {
|
|
return axis->stop(0.0);
|
|
} else {
|
|
return asynMotorController::writeInt32(pasynUser, value);
|
|
}
|
|
}
|
|
|
|
asynStatus sinqController::readInt32(asynUser *pasynUser, epicsInt32 *value) {
|
|
|
|
// Casting into a sinqAxis is necessary to get access to the field axisNo()
|
|
asynMotorAxis *asynAxis = getAxis(pasynUser);
|
|
sinqAxis *axis = dynamic_cast<sinqAxis *>(asynAxis);
|
|
|
|
if (axis == nullptr) {
|
|
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\", axis %d => %s, line %d:\nAxis is not an "
|
|
"instance of sinqAxis.\n",
|
|
portName, axis->axisNo(), __PRETTY_FUNCTION__, __LINE__);
|
|
return asynError;
|
|
}
|
|
|
|
if (pasynUser->reason == motorEnableRBV_) {
|
|
return getIntegerParam(axis->axisNo(), motorEnableRBV_, value);
|
|
} else if (pasynUser->reason == motorCanDisable_) {
|
|
return getIntegerParam(axis->axisNo(), motorCanDisable_, value);
|
|
} else {
|
|
return asynMotorController::readInt32(pasynUser, value);
|
|
}
|
|
}
|
|
|
|
asynStatus sinqController::couldNotParseResponse(const char *command,
|
|
const char *response,
|
|
int axisNo,
|
|
const char *functionName,
|
|
int line) {
|
|
asynStatus pl_status = asynSuccess;
|
|
|
|
asynPrint(pasynOctetSyncIOipPort_, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\", axis %d => %s, line %d:\nCould not interpret "
|
|
"response \"%s\" for command \"%s\".\n",
|
|
portName, axisNo, functionName, line, response, command);
|
|
|
|
pl_status = setStringParam(
|
|
motorMessageText_,
|
|
"Could not interpret controller response. Please call the support");
|
|
if (pl_status != asynSuccess) {
|
|
return paramLibAccessFailed(pl_status, "motorMessageText_", axisNo,
|
|
__PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
|
|
pl_status = setIntegerParam(motorStatusCommsError_, 1);
|
|
if (pl_status != asynSuccess) {
|
|
return paramLibAccessFailed(pl_status, "motorStatusCommsError_", axisNo,
|
|
__PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
|
|
return asynError;
|
|
}
|
|
|
|
asynStatus sinqController::paramLibAccessFailed(asynStatus status,
|
|
const char *parameter,
|
|
int axisNo,
|
|
const char *functionName,
|
|
int line) {
|
|
|
|
if (status != asynSuccess) {
|
|
asynPrint(pasynOctetSyncIOipPort_, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\", axis %d => %s, line %d:\n Accessing the "
|
|
"parameter library failed for parameter %s with error %s.\n",
|
|
portName, axisNo, functionName, line, parameter,
|
|
stringifyAsynStatus(status));
|
|
|
|
// Log the error message and try to propagate it. If propagating fails,
|
|
// there is nothing we can do here anyway.
|
|
setStringParam(motorMessageText_,
|
|
"Accessing paramLib failed. Please call the support.");
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
asynStatus sinqController::checkComTimeoutWatchdog(int axisNo,
|
|
char *motorMessage,
|
|
size_t motorMessageSize) {
|
|
|
|
asynStatus paramLibStatus = asynSuccess;
|
|
|
|
// Add a new timeout event to the queue
|
|
timeoutEvents_.push_back(time(NULL));
|
|
|
|
// Remove every event which is older than the time window from the deque
|
|
while (1) {
|
|
if (timeoutEvents_.empty()) {
|
|
break;
|
|
}
|
|
if (timeoutEvents_[0] + comTimeoutWindow_ <= time(NULL)) {
|
|
timeoutEvents_.pop_front();
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Check if the maximum allowed number of events has been exceeded
|
|
bool wantToPrint = timeoutEvents_.size() > maxNumberTimeouts_;
|
|
|
|
if (msgPrintControl_.shouldBePrinted(portName, axisNo, __PRETTY_FUNCTION__,
|
|
__LINE__, wantToPrint,
|
|
pasynUserSelf)) {
|
|
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\", axis %d => %s, line %d:\nMore than %ld "
|
|
"communication timeouts in %ld "
|
|
"seconds.%s\n",
|
|
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
|
|
maxNumberTimeouts_, comTimeoutWindow_,
|
|
msgPrintControl_.getSuffix());
|
|
}
|
|
|
|
if (wantToPrint) {
|
|
snprintf(motorMessage, motorMessageSize,
|
|
"More than %ld communication timeouts in %ld seconds. Please "
|
|
"call the support.",
|
|
maxNumberTimeouts_, comTimeoutWindow_);
|
|
|
|
paramLibStatus = setIntegerParam(motorStatusCommsError_, 1);
|
|
if (paramLibStatus != asynSuccess) {
|
|
return paramLibAccessFailed(paramLibStatus,
|
|
"motorStatusCommsError_", axisNo,
|
|
__PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
|
|
return asynError;
|
|
} else {
|
|
return asynSuccess;
|
|
}
|
|
}
|
|
|
|
asynStatus sinqController::checkComTimeoutWatchdog(sinqAxis *axis) {
|
|
|
|
char motorMessage[200] = {0};
|
|
|
|
asynStatus status =
|
|
checkComTimeoutWatchdog(axis->axisNo(), motorMessage, 200);
|
|
if (status == asynError) {
|
|
status = axis->setStringParam(motorMessageText_, motorMessage);
|
|
if (status != asynSuccess) {
|
|
return paramLibAccessFailed(status, "motorMessageText_",
|
|
axis->axisNo(), __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
asynStatus sinqController::checkMaxSubsequentTimeouts(int timeoutNo, int axisNo,
|
|
char *motorMessage,
|
|
size_t motorMessageSize) {
|
|
asynStatus paramLibStatus = asynSuccess;
|
|
|
|
if (timeoutNo >= maxSubsequentTimeouts_) {
|
|
if (!maxSubsequentTimeoutsExceeded_) {
|
|
snprintf(motorMessage, motorMessageSize,
|
|
"Communication timeout between IOC and motor controller. "
|
|
"Trying to reconnect ...");
|
|
asynPrint(
|
|
this->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\", axis %d => %s, line %d:\nMore than %d "
|
|
"subsequent communication "
|
|
"timeouts\n",
|
|
this->portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
|
|
maxSubsequentTimeouts_);
|
|
|
|
paramLibStatus = setIntegerParam(motorStatusCommsError_, 1);
|
|
if (paramLibStatus != asynSuccess) {
|
|
return paramLibAccessFailed(paramLibStatus,
|
|
"motorStatusCommsError_", axisNo,
|
|
__PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
maxSubsequentTimeoutsExceeded_ = true;
|
|
}
|
|
|
|
return asynError;
|
|
} else {
|
|
maxSubsequentTimeoutsExceeded_ = false;
|
|
motorMessage[0] = '\0';
|
|
return asynSuccess;
|
|
}
|
|
}
|
|
|
|
asynStatus sinqController::checkMaxSubsequentTimeouts(int timeoutNo,
|
|
sinqAxis *axis) {
|
|
|
|
char motorMessage[200] = {0};
|
|
|
|
asynStatus status = checkMaxSubsequentTimeouts(axis->axisNo(), timeoutNo,
|
|
motorMessage, 200);
|
|
if (status == asynError) {
|
|
status = axis->setStringParam(motorMessageText_, motorMessage);
|
|
if (status != asynSuccess) {
|
|
return paramLibAccessFailed(status, "motorMessageText_",
|
|
axis->axisNo(), __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
// Static pointers (valid for the entire lifetime of the IOC). The number behind
|
|
// the strings gives the integer number of each variant (see also method
|
|
// stringifyAsynStatus)
|
|
const char asynSuccessStringified[] = "success"; // 0
|
|
const char asynTimeoutStringified[] = "timeout"; // 1
|
|
const char asynOverflowStringified[] = "overflow"; // 2
|
|
const char asynErrorStringified[] = "error"; // 3
|
|
const char asynDisconnectedStringified[] = "disconnected"; // 4
|
|
const char asynDisabledStringified[] = "disabled"; // 5
|
|
const char asynParamAlreadyExistsStringified[] =
|
|
"parameter already exists"; // 6
|
|
const char asynParamNotFoundStringified[] = "parameter not found"; // 7
|
|
const char asynParamWrongTypeStringified[] = "wrong type"; // 8
|
|
const char asynParamBadIndexStringified[] = "bad index"; // 9
|
|
const char asynParamUndefinedStringified[] = "parameter undefined"; // 10
|
|
const char asynParamInvalidListStringified[] = "invalid list"; // 11
|
|
const char inputDidNotMatchAsynStatus[] =
|
|
"Input did not match any variant of asynStatus";
|
|
|
|
const char *sinqController::stringifyAsynStatus(asynStatus status) {
|
|
// See
|
|
// https://github.com/epics-modules/asyn/blob/master/asyn/asynDriver/asynDriver.h
|
|
// and
|
|
// https://github.com/epics-modules/asyn/blob/master/asyn/asynPortDriver/paramErrors.h
|
|
// for the definition of the error codes
|
|
// The pragma is necessary since the param lib error codes are "tacked onto"
|
|
// the enum, which results in compiler warnings otherwise.
|
|
#pragma GCC diagnostic ignored "-Wswitch"
|
|
switch (status) {
|
|
case asynSuccess:
|
|
return asynSuccessStringified;
|
|
case asynTimeout:
|
|
return asynTimeoutStringified;
|
|
case asynOverflow:
|
|
return asynOverflowStringified;
|
|
case asynError:
|
|
return asynErrorStringified;
|
|
case asynDisconnected:
|
|
return asynDisconnectedStringified;
|
|
case asynDisabled:
|
|
return asynDisabledStringified;
|
|
case asynParamAlreadyExists:
|
|
return asynParamAlreadyExistsStringified;
|
|
case asynParamNotFound:
|
|
return asynParamNotFoundStringified;
|
|
case asynParamWrongType:
|
|
return asynParamWrongTypeStringified;
|
|
case asynParamBadIndex:
|
|
return asynParamBadIndexStringified;
|
|
case asynParamUndefined:
|
|
return asynParamUndefinedStringified;
|
|
case asynParamInvalidList:
|
|
return asynParamInvalidListStringified;
|
|
}
|
|
|
|
asynPrint(pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"%s, line %d:\nInput did not match any "
|
|
"variant of asynStatus.\n",
|
|
__PRETTY_FUNCTION__, __LINE__);
|
|
|
|
return inputDidNotMatchAsynStatus;
|
|
}
|
|
|
|
// =============================================================================
|
|
// IOC shell functions
|
|
extern "C" {
|
|
|
|
// =============================================================================
|
|
|
|
/**
|
|
* @brief Set the threshold for the communication timeout frequency (FFI
|
|
* implementation)
|
|
*
|
|
* @param comTimeoutWindow Size of the time window used to calculate
|
|
* the moving average of timeout events in seconds. 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
|
|
*/
|
|
asynStatus setThresholdComTimeout(const char *portName, int comTimeoutWindow,
|
|
int maxNumberTimeouts) {
|
|
|
|
sinqController *pC;
|
|
pC = (sinqController *)findAsynPortDriver(portName);
|
|
if (pC == nullptr) {
|
|
errlogPrintf("Controller \"%s\" => %s, line %d:\nPort %s not found.",
|
|
portName, __PRETTY_FUNCTION__, __LINE__, portName);
|
|
return asynError;
|
|
}
|
|
|
|
return pC->setThresholdComTimeout(comTimeoutWindow, maxNumberTimeouts);
|
|
}
|
|
|
|
static const iocshArg setThresholdComTimeoutArg0 = {"Controller port name",
|
|
iocshArgString};
|
|
static const iocshArg setThresholdComTimeoutArg1 = {
|
|
"Time window communication timeout frequency", iocshArgInt};
|
|
static const iocshArg setThresholdComTimeoutArg2 = {
|
|
"Maximum allowed number of communication timeouts within the window",
|
|
iocshArgInt};
|
|
static const iocshArg *const setThresholdComTimeoutArgs[] = {
|
|
&setThresholdComTimeoutArg0, &setThresholdComTimeoutArg1,
|
|
&setThresholdComTimeoutArg2};
|
|
static const iocshFuncDef setThresholdComTimeoutDef = {
|
|
"setThresholdComTimeout", 3, setThresholdComTimeoutArgs};
|
|
|
|
static void setThresholdComTimeoutCallFunc(const iocshArgBuf *args) {
|
|
setThresholdComTimeout(args[0].sval, args[1].ival, args[2].ival);
|
|
}
|
|
|
|
// =============================================================================
|
|
|
|
/**
|
|
* @brief Set the maximum number of subsequent timeouts (FFI implementation)
|
|
*
|
|
* @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
|
|
*/
|
|
asynStatus setMaxSubsequentTimeouts(const char *portName,
|
|
int maxSubsequentTimeouts) {
|
|
void *ptr = findAsynPortDriver(portName);
|
|
if (ptr == nullptr) {
|
|
/*
|
|
We can't use asynPrint here since this macro would require us
|
|
to get a pasynOctetSyncIOipPort_ from a pointer to an asynPortDriver.
|
|
However, the given pointer is a nullptr and therefore doesn't
|
|
have a pasynOctetSyncIOipPort_! printf is an EPICS alternative which
|
|
works w/o that, but doesn't offer the comfort provided
|
|
by the asynTrace-facility
|
|
*/
|
|
errlogPrintf("Controller \"%s\" => %s, line %d:\nPort %s not found.",
|
|
portName, __PRETTY_FUNCTION__, __LINE__, portName);
|
|
return asynError;
|
|
}
|
|
// Unsafe cast of the pointer to an asynPortDriver
|
|
asynPortDriver *apd = (asynPortDriver *)(ptr);
|
|
|
|
// Safe downcast
|
|
sinqController *pC = dynamic_cast<sinqController *>(apd);
|
|
if (pC == nullptr) {
|
|
errlogPrintf(
|
|
"Controller \"%s\" => %s, line %d:\ncontroller on port %s is not a "
|
|
"turboPmacController.",
|
|
portName, __PRETTY_FUNCTION__, __LINE__, portName);
|
|
return asynError;
|
|
}
|
|
|
|
// Set the new value
|
|
pC->setMaxSubsequentTimeouts(maxSubsequentTimeouts);
|
|
|
|
return asynSuccess;
|
|
}
|
|
|
|
static const iocshArg SetMaxSubsequentTimeoutsArg0 = {
|
|
"Controller name (e.g. mcu1)", iocshArgString};
|
|
static const iocshArg SetMaxSubsequentTimeoutsArg1 = {
|
|
"Maximum number of subsequent timeouts before the user receives an error "
|
|
"message",
|
|
iocshArgInt};
|
|
static const iocshArg *const SetMaxSubsequentTimeoutsArgs[] = {
|
|
&SetMaxSubsequentTimeoutsArg0, &SetMaxSubsequentTimeoutsArg1};
|
|
static const iocshFuncDef setMaxSubsequentTimeoutsDef = {
|
|
"setMaxSubsequentTimeouts", 2, SetMaxSubsequentTimeoutsArgs};
|
|
static void setMaxSubsequentTimeoutsCallFunc(const iocshArgBuf *args) {
|
|
setMaxSubsequentTimeouts(args[0].sval, args[1].ival);
|
|
}
|
|
|
|
// =============================================================================
|
|
|
|
// This function is made known to EPICS in sinqMotor.dbd and is called by EPICS
|
|
// in order to register all functions in the IOC shell
|
|
static void sinqControllerRegister(void) {
|
|
iocshRegister(&setThresholdComTimeoutDef, setThresholdComTimeoutCallFunc);
|
|
iocshRegister(&setMaxSubsequentTimeoutsDef,
|
|
setMaxSubsequentTimeoutsCallFunc);
|
|
}
|
|
epicsExportRegistrar(sinqControllerRegister);
|
|
|
|
} // extern C
|