1235 lines
46 KiB
C++
1235 lines
46 KiB
C++
#include "masterMacsAxis.h"
|
|
#include "asynOctetSyncIO.h"
|
|
#include "epicsExport.h"
|
|
#include "iocsh.h"
|
|
#include "masterMacsController.h"
|
|
#include <bitset>
|
|
#include <cmath>
|
|
#include <errlog.h>
|
|
#include <limits>
|
|
#include <math.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
/*
|
|
Contains all instances of turboPmacAxis which have been created and is used in
|
|
the initialization hook function.
|
|
*/
|
|
static std::vector<masterMacsAxis *> axes;
|
|
|
|
/**
|
|
* @brief Hook function to perform certain actions during the IOC initialization
|
|
*
|
|
* @param iState
|
|
*/
|
|
static void epicsInithookFunction(initHookState iState) {
|
|
if (iState == initHookAfterDatabaseRunning) {
|
|
// Iterate through all axes of each and call the initialization method
|
|
// on each one of them.
|
|
for (std::vector<masterMacsAxis *>::iterator itA = axes.begin();
|
|
itA != axes.end(); ++itA) {
|
|
masterMacsAxis *axis = *itA;
|
|
axis->init();
|
|
}
|
|
}
|
|
}
|
|
|
|
void appendErrorMessage(char *fullMessage, size_t capacityFullMessage,
|
|
const char *toBeAppended) {
|
|
size_t lenFullMessage = strlen(fullMessage);
|
|
size_t lenToBeAppended = strlen(toBeAppended);
|
|
|
|
if (lenFullMessage == 0) {
|
|
// The error message is empty -> Just copy the content of toBeAppended
|
|
// into fullMessage, if the formers capacity suffices
|
|
if (lenToBeAppended < capacityFullMessage) {
|
|
// cppcheck-suppress unsafeStrcpy
|
|
strcpy(fullMessage, toBeAppended);
|
|
}
|
|
} else {
|
|
// Append the message and add a linebreak in between, if the capacity of
|
|
// fullMessage suffices. We need capacity for one additional character
|
|
// because of the linebreak.
|
|
if (lenFullMessage + lenToBeAppended + 1 < capacityFullMessage) {
|
|
// Append the linebreak and readd the null terminator behind it
|
|
// fullMessage[lenFullMessage] = '\n';
|
|
// fullMessage[lenFullMessage + 1] = '\0';
|
|
|
|
// Append the actual message
|
|
strcat(fullMessage, toBeAppended);
|
|
}
|
|
}
|
|
}
|
|
|
|
masterMacsAxis::masterMacsAxis(masterMacsController *pC, int axisNo)
|
|
: sinqAxis(pC, axisNo), pC_(pC) {
|
|
|
|
asynStatus status = asynSuccess;
|
|
|
|
/*
|
|
The superclass constructor sinqAxis calls in turn its superclass constructor
|
|
asynMotorAxis. In the latter, a pointer to the constructed object this is
|
|
stored inside the array pAxes_:
|
|
|
|
pC->pAxes_[axisNo] = this;
|
|
|
|
Therefore, the axes are managed by the controller pC. If axisNo is out of
|
|
bounds, asynMotorAxis prints an error. However, we want the IOC creation to
|
|
stop completely, since this is a configuration error.
|
|
*/
|
|
if (axisNo >= pC->numAxes_) {
|
|
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\", axis %d => %s, line %d:: FATAL ERROR: "
|
|
"Axis index %d must be smaller than the total number of axes "
|
|
"%d. Call the support.",
|
|
pC_->portName, axisNo, __PRETTY_FUNCTION__, __LINE__, axisNo_,
|
|
pC->numAxes_);
|
|
exit(-1);
|
|
}
|
|
|
|
// Register the hook function during construction of the first axis object
|
|
if (axes.empty()) {
|
|
initHookRegister(&epicsInithookFunction);
|
|
}
|
|
|
|
// Collect all axes into this list which will be used in the hook function
|
|
axes.push_back(this);
|
|
|
|
// Initialize all member variables
|
|
axisStatus_ = std::bitset<16>(0);
|
|
axisError_ = std::bitset<16>(0);
|
|
|
|
// Initial value for the motor speed, is overwritten in atFirstPoll.
|
|
lastSetSpeed_ = 0.0;
|
|
|
|
// Flag for handshake waiting
|
|
waitForHandshake_ = false;
|
|
timeAtHandshake_ = 0;
|
|
|
|
// masterMacs motors can always be disabled
|
|
status = pC_->setIntegerParam(axisNo_, pC_->motorCanDisable_, 1);
|
|
if (status != asynSuccess) {
|
|
asynPrint(
|
|
pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\", axis %d => %s, line %d:\nFATAL ERROR "
|
|
"(setting a parameter value failed with %s)\n. Terminating IOC",
|
|
pC_->portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
|
|
pC_->stringifyAsynStatus(status));
|
|
exit(-1);
|
|
}
|
|
|
|
// Assume that the motor is initially not moving
|
|
status = pC_->setIntegerParam(axisNo_, pC_->motorStatusMoving_, false);
|
|
if (status != asynSuccess) {
|
|
asynPrint(
|
|
pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\", axis %d => %s, line %d:\nFATAL ERROR "
|
|
"(setting a parameter value failed with %s)\n. Terminating IOC",
|
|
pC_->portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
|
|
pC_->stringifyAsynStatus(status));
|
|
}
|
|
}
|
|
|
|
masterMacsAxis::~masterMacsAxis(void) {
|
|
// Since the controller memory is managed somewhere else, we don't need to
|
|
// clean up the pointer pC here.
|
|
}
|
|
|
|
/**
|
|
Read out the following values:
|
|
- current position
|
|
- current velocity
|
|
- maximum velocity
|
|
- acceleration
|
|
- Software limits
|
|
*/
|
|
asynStatus masterMacsAxis::init() {
|
|
|
|
// Local variable declaration
|
|
asynStatus pl_status = asynSuccess;
|
|
char response[pC_->MAXBUF_];
|
|
int nvals = 0;
|
|
double motorRecResolution = 0.0;
|
|
double motorPosition = 0.0;
|
|
double motorVelocity = 0.0;
|
|
double motorVmax = 0.0;
|
|
double motorAccel = 0.0;
|
|
|
|
// =========================================================================
|
|
|
|
// The parameter library takes some time to be initialized. Therefore we
|
|
// wait until the status is not asynParamUndefined anymore.
|
|
time_t now = time(NULL);
|
|
time_t maxInitTime = 60;
|
|
while (1) {
|
|
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution_,
|
|
&motorRecResolution);
|
|
if (pl_status == asynParamUndefined) {
|
|
if (now + maxInitTime < time(NULL)) {
|
|
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\", axis %d => %s, line "
|
|
"%d\nInitializing the parameter library failed.\n",
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
return asynError;
|
|
}
|
|
} else if (pl_status == asynSuccess) {
|
|
break;
|
|
} else if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorRecResolution_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
}
|
|
|
|
// Read out the current position
|
|
pl_status = pC_->read(axisNo_, 12, response);
|
|
if (pl_status != asynSuccess) {
|
|
return pl_status;
|
|
}
|
|
nvals = sscanf(response, "%lf", &motorPosition);
|
|
if (nvals != 1) {
|
|
return pC_->errMsgCouldNotParseResponse("R12", response, axisNo_,
|
|
__PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
|
|
// Read out the current velocity
|
|
pl_status = pC_->read(axisNo_, 05, response);
|
|
if (pl_status != asynSuccess) {
|
|
return pl_status;
|
|
}
|
|
nvals = sscanf(response, "%lf", &motorVelocity);
|
|
if (nvals != 1) {
|
|
return pC_->errMsgCouldNotParseResponse("R05", response, axisNo_,
|
|
__PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
|
|
// Read out the maximum velocity
|
|
pl_status = pC_->read(axisNo_, 26, response);
|
|
if (pl_status != asynSuccess) {
|
|
return pl_status;
|
|
}
|
|
nvals = sscanf(response, "%lf", &motorVmax);
|
|
if (nvals != 1) {
|
|
return pC_->errMsgCouldNotParseResponse("R26", response, axisNo_,
|
|
__PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
|
|
// Read out the acceleration
|
|
pl_status = pC_->read(axisNo_, 06, response);
|
|
if (pl_status != asynSuccess) {
|
|
return pl_status;
|
|
}
|
|
nvals = sscanf(response, "%lf", &motorAccel);
|
|
if (nvals != 1) {
|
|
return pC_->errMsgCouldNotParseResponse("R06", response, axisNo_,
|
|
__PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
|
|
// Cache the motor speed. If this value differs from the one in the motor
|
|
// record at the start of a movement, the motor record value is sent to
|
|
// MasterMACS.
|
|
lastSetSpeed_ = motorVelocity;
|
|
|
|
// Transform from motor to parameter library coordinates
|
|
motorPosition = motorPosition / motorRecResolution;
|
|
|
|
// Store these values in the parameter library
|
|
pl_status =
|
|
pC_->setDoubleParam(axisNo_, pC_->motorPosition_, motorPosition);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorPosition_", axisNo_,
|
|
__PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
|
|
// Write to the motor record fields
|
|
pl_status = setVeloFields(motorVelocity, 0.0, motorVmax);
|
|
if (pl_status != asynSuccess) {
|
|
return pl_status;
|
|
}
|
|
pl_status = setAcclField(motorAccel);
|
|
if (pl_status != asynSuccess) {
|
|
return pl_status;
|
|
}
|
|
|
|
pl_status = readEncoderType();
|
|
if (pl_status != asynSuccess) {
|
|
return pl_status;
|
|
}
|
|
|
|
// Update the parameter library immediately
|
|
pl_status = callParamCallbacks();
|
|
if (pl_status != asynSuccess) {
|
|
// If we can't communicate with the parameter library, it doesn't
|
|
// make sense to try and upstream this to the user -> Just log the
|
|
// error
|
|
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\", axis %d => %s, line "
|
|
"%d:\ncallParamCallbacks failed with %s.\n",
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
pC_->stringifyAsynStatus(pl_status));
|
|
return pl_status;
|
|
}
|
|
return pl_status;
|
|
}
|
|
|
|
// Perform the actual poll
|
|
asynStatus masterMacsAxis::doPoll(bool *moving) {
|
|
|
|
// Return value for the poll
|
|
asynStatus poll_status = asynSuccess;
|
|
|
|
// Status of read-write-operations of ASCII commands to the controller
|
|
asynStatus rw_status = asynSuccess;
|
|
|
|
// Status of parameter library operations
|
|
asynStatus pl_status = asynSuccess;
|
|
|
|
char response[pC_->MAXBUF_];
|
|
int nvals = 0;
|
|
|
|
int direction = 0;
|
|
bool hasError = false;
|
|
double currentPosition = 0.0;
|
|
double previousPosition = 0.0;
|
|
double motorRecResolution = 0.0;
|
|
double highLimit = 0.0;
|
|
double lowLimit = 0.0;
|
|
double limitsOffset = 0.0;
|
|
double handshakePerformed = 0;
|
|
|
|
// =========================================================================
|
|
|
|
// Are we currently waiting for a handshake?
|
|
if (waitForHandshake_) {
|
|
pC_->read(axisNo_, 86, response);
|
|
if (rw_status != asynSuccess) {
|
|
return rw_status;
|
|
}
|
|
|
|
nvals = sscanf(response, "%lf", &handshakePerformed);
|
|
if (nvals != 1) {
|
|
return pC_->errMsgCouldNotParseResponse(
|
|
"R86", response, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
|
|
if (handshakePerformed == 1.0) {
|
|
// Handshake has been performed successfully -> Continue with the
|
|
// poll
|
|
waitForHandshake_ = false;
|
|
} else {
|
|
// Still waiting for the handshake - try again in the next busy
|
|
// poll. This is already part of the movement procedure.
|
|
*moving = true;
|
|
|
|
pl_status = setIntegerParam(pC_->motorStatusMoving_, *moving);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status,
|
|
"motorStatusMoving_", axisNo_,
|
|
__PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
|
|
pl_status = setIntegerParam(pC_->motorStatusDone_, !(*moving));
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorStatusDone_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
|
|
return asynSuccess;
|
|
}
|
|
}
|
|
|
|
// Motor resolution from parameter library
|
|
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution_,
|
|
&motorRecResolution);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorRecResolution_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
|
|
// Read the previous motor position
|
|
pl_status =
|
|
pC_->getDoubleParam(axisNo_, pC_->motorPosition_, &previousPosition);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorPosition_", axisNo_,
|
|
__PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
|
|
// Transform from EPICS to motor coordinates (see comment in
|
|
// masterMacsAxis::readConfig())
|
|
previousPosition = previousPosition * motorRecResolution;
|
|
|
|
// Update the axis status
|
|
rw_status = readAxisStatus();
|
|
if (rw_status != asynSuccess) {
|
|
return rw_status;
|
|
}
|
|
|
|
// If the motor is switched off or if it reached its target, it is not
|
|
// moving.
|
|
if (targetReached() || !switchedOn()) {
|
|
*moving = false;
|
|
} else {
|
|
*moving = true;
|
|
}
|
|
|
|
// Read the current position
|
|
rw_status = pC_->read(axisNo_, 12, response);
|
|
if (rw_status != asynSuccess) {
|
|
return rw_status;
|
|
}
|
|
nvals = sscanf(response, "%lf", ¤tPosition);
|
|
if (nvals != 1) {
|
|
return pC_->errMsgCouldNotParseResponse("R12", response, axisNo_,
|
|
__PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
|
|
/*
|
|
Read out the error if either a fault condition status flag has been set or
|
|
if a movement has just ended.
|
|
*/
|
|
msgPrintControlKey keyError = msgPrintControlKey(
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
|
|
|
if (faultConditionSet() || !(*moving)) {
|
|
rw_status = readAxisError();
|
|
|
|
/*
|
|
A communication error is a special case. If a communication between
|
|
controller and axis error occurred, all subsequent errors are ignored,
|
|
since this information is not reliable.
|
|
*/
|
|
if (communicationError()) {
|
|
if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
|
|
pC_->pasynUserSelf)) {
|
|
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\", axis %d => %s, line "
|
|
"%d\nCommunication error.%s\n",
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
pC_->msgPrintControl_.getSuffix());
|
|
}
|
|
|
|
pl_status =
|
|
setStringParam(pC_->motorMessageText_,
|
|
"Communication error between PC and motor "
|
|
"controller. Please call the support.");
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
|
|
poll_status = asynError;
|
|
} else {
|
|
|
|
// This buffer must be initialized to zero because we build the
|
|
// error message by appending strings.
|
|
char userMessage[pC_->MAXBUF_] = {0};
|
|
char shellMessage[pC_->MAXBUF_] = {0};
|
|
|
|
// Concatenate all other errors
|
|
if (shortCircuit()) {
|
|
appendErrorMessage(shellMessage, sizeof(shellMessage),
|
|
"Short circuit fault.");
|
|
appendErrorMessage(
|
|
userMessage, sizeof(userMessage),
|
|
"Short circuit error. Please call the support.");
|
|
|
|
poll_status = asynError;
|
|
}
|
|
|
|
if (encoderError()) {
|
|
appendErrorMessage(shellMessage, sizeof(shellMessage),
|
|
"Encoder error.");
|
|
appendErrorMessage(userMessage, sizeof(userMessage),
|
|
"Encoder error. Please call the support.");
|
|
|
|
poll_status = asynError;
|
|
}
|
|
|
|
if (followingError()) {
|
|
appendErrorMessage(
|
|
shellMessage, sizeof(shellMessage),
|
|
"Maximum callowed following error exceeded.");
|
|
appendErrorMessage(
|
|
userMessage, sizeof(userMessage),
|
|
"Maximum allowed following error exceeded.Check if "
|
|
"movement range is blocked. Otherwise please call the "
|
|
"support.");
|
|
|
|
poll_status = asynError;
|
|
}
|
|
|
|
if (feedbackError()) {
|
|
appendErrorMessage(shellMessage, sizeof(shellMessage),
|
|
"Feedback error.");
|
|
appendErrorMessage(userMessage, sizeof(userMessage),
|
|
"Feedback error. Please call the support.");
|
|
|
|
poll_status = asynError;
|
|
}
|
|
|
|
/*
|
|
Either the software limits or the end switches of the controller
|
|
have been hit. Since the EPICS limits are derived from the software
|
|
limits and are a little bit smaller, these error cases can only
|
|
happen if either the axis has an incremental encoder which is not
|
|
properly homed or if a bug occured.
|
|
*/
|
|
if (positiveLimitSwitch() || negativeLimitSwitch() ||
|
|
positiveSoftwareLimit() || negativeSoftwareLimit()) {
|
|
|
|
// Distinction for developers
|
|
if (positiveLimitSwitch()) {
|
|
appendErrorMessage(shellMessage, sizeof(shellMessage),
|
|
"Positive limit switch.");
|
|
}
|
|
if (negativeLimitSwitch()) {
|
|
appendErrorMessage(shellMessage, sizeof(shellMessage),
|
|
"Negative limit switch.");
|
|
}
|
|
if (positiveSoftwareLimit()) {
|
|
appendErrorMessage(shellMessage, sizeof(shellMessage),
|
|
"Positive software limit.");
|
|
}
|
|
if (negativeSoftwareLimit()) {
|
|
appendErrorMessage(shellMessage, sizeof(shellMessage),
|
|
"Negative software limit.");
|
|
}
|
|
|
|
// Generic error message for user
|
|
appendErrorMessage(
|
|
userMessage, sizeof(userMessage),
|
|
"Software limits or end switch hit. Try homing the motor, "
|
|
"moving in the opposite direction or check the SPS for "
|
|
"errors (if available). Otherwise please call the "
|
|
"support.");
|
|
|
|
poll_status = asynError;
|
|
}
|
|
|
|
if (overCurrent()) {
|
|
appendErrorMessage(shellMessage, sizeof(shellMessage),
|
|
"Overcurrent error.");
|
|
appendErrorMessage(
|
|
userMessage, sizeof(userMessage),
|
|
"Overcurrent error. Please call the support.");
|
|
|
|
poll_status = asynError;
|
|
}
|
|
|
|
if (overTemperature()) {
|
|
appendErrorMessage(shellMessage, sizeof(shellMessage),
|
|
"Overtemperature error.");
|
|
appendErrorMessage(
|
|
userMessage, sizeof(userMessage),
|
|
"Overtemperature error. Please call the support.");
|
|
|
|
poll_status = asynError;
|
|
}
|
|
|
|
if (overVoltage()) {
|
|
appendErrorMessage(shellMessage, sizeof(shellMessage),
|
|
"Overvoltage error.");
|
|
appendErrorMessage(
|
|
userMessage, sizeof(userMessage),
|
|
"Overvoltage error. Please call the support.");
|
|
|
|
poll_status = asynError;
|
|
}
|
|
|
|
if (underVoltage()) {
|
|
appendErrorMessage(shellMessage, sizeof(shellMessage),
|
|
"Undervoltage error.");
|
|
appendErrorMessage(
|
|
userMessage, sizeof(userMessage),
|
|
"Undervoltage error. Please call the support.");
|
|
|
|
poll_status = asynError;
|
|
}
|
|
|
|
if (stoFault()) {
|
|
appendErrorMessage(shellMessage, sizeof(shellMessage),
|
|
"STO input is on disable state.");
|
|
appendErrorMessage(userMessage, sizeof(userMessage),
|
|
"STO fault. Please call the support.");
|
|
|
|
poll_status = asynError;
|
|
}
|
|
|
|
if (strlen(shellMessage) > 0) {
|
|
if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
|
|
pC_->pasynUserSelf)) {
|
|
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\", axis %d => %s, line "
|
|
"%d\n%s.%s\n",
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__, shellMessage,
|
|
pC_->msgPrintControl_.getSuffix());
|
|
}
|
|
}
|
|
|
|
pl_status = setStringParam(pC_->motorMessageText_, userMessage);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
}
|
|
} else {
|
|
pC_->msgPrintControl_.resetCount(keyError);
|
|
}
|
|
|
|
// Read out the limits, if the motor is not moving
|
|
if (!(*moving)) {
|
|
rw_status = pC_->read(axisNo_, 34, response);
|
|
if (rw_status != asynSuccess) {
|
|
return rw_status;
|
|
}
|
|
nvals = sscanf(response, "%lf", &lowLimit);
|
|
if (nvals != 1) {
|
|
return pC_->errMsgCouldNotParseResponse(
|
|
"R34", response, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
|
|
rw_status = pC_->read(axisNo_, 33, response);
|
|
if (rw_status != asynSuccess) {
|
|
return rw_status;
|
|
}
|
|
nvals = sscanf(response, "%lf", &highLimit);
|
|
if (nvals != 1) {
|
|
return pC_->errMsgCouldNotParseResponse(
|
|
"R33", response, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
|
|
/*
|
|
The axis limits are set as: ({[]})
|
|
where [] are the positive and negative limits set in EPICS/NICOS, {} are
|
|
the software limits set on the MCU and () are the hardware limit
|
|
switches. In other words, the EPICS/NICOS limits must be stricter than
|
|
the software limits on the MCU which in turn should be stricter than the
|
|
hardware limit switches. For example, if the hardware limit switches are
|
|
at [-10, 10], the software limits could be at [-9, 9] and the EPICS /
|
|
NICOS limits could be at
|
|
[-8, 8]. Therefore, we cannot use the software limits read from the MCU
|
|
directly, but need to shrink them a bit. In this case, we're shrinking
|
|
them by 0.1 mm or 0.1 degree (depending on the axis type) on both sides.
|
|
*/
|
|
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorLimitsOffset_,
|
|
&limitsOffset);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorLimitsOffset_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
highLimit = highLimit - limitsOffset;
|
|
lowLimit = lowLimit + limitsOffset;
|
|
|
|
pl_status = pC_->setDoubleParam(axisNo_, pC_->motorHighLimitFromDriver_,
|
|
highLimit);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(
|
|
pl_status, "motorHighLimitFromDriver_", axisNo_,
|
|
__PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
|
|
pl_status = pC_->setDoubleParam(axisNo_, pC_->motorLowLimitFromDriver_,
|
|
lowLimit);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorLowLimit_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
}
|
|
|
|
// Update the enable PV
|
|
pl_status = setIntegerParam(pC_->motorEnableRBV_,
|
|
readyToBeSwitchedOn() && switchedOn());
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorEnableRBV_", axisNo_,
|
|
__PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
|
|
if (*moving) {
|
|
// If the axis is moving, evaluate the movement direction
|
|
if ((currentPosition - previousPosition) > 0) {
|
|
direction = 1;
|
|
} else {
|
|
direction = 0;
|
|
}
|
|
}
|
|
|
|
// Update the parameter library
|
|
if (hasError) {
|
|
pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
}
|
|
pl_status = setIntegerParam(pC_->motorStatusMoving_, *moving);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorStatusMoving_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
|
|
pl_status = setIntegerParam(pC_->motorStatusDone_, !(*moving));
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorStatusDone_", axisNo_,
|
|
__PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
|
|
pl_status = setIntegerParam(pC_->motorStatusDirection_, direction);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorStatusDirection_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
|
|
// Transform from motor to EPICS coordinates (see comment in
|
|
// masterMacsAxis::init())
|
|
currentPosition = currentPosition / motorRecResolution;
|
|
|
|
pl_status = setDoubleParam(pC_->motorPosition_, currentPosition);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorPosition_", axisNo_,
|
|
__PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
|
|
return poll_status;
|
|
}
|
|
|
|
asynStatus masterMacsAxis::doMove(double position, int relative,
|
|
double minVelocity, double maxVelocity,
|
|
double acceleration) {
|
|
|
|
// Status of read-write-operations of ASCII commands to the controller
|
|
asynStatus rw_status = asynSuccess;
|
|
|
|
// Status of parameter library operations
|
|
asynStatus pl_status = asynSuccess;
|
|
|
|
char value[pC_->MAXBUF_];
|
|
double motorCoordinatesPosition = 0.0;
|
|
double motorRecResolution = 0.0;
|
|
double motorVelocity = 0.0;
|
|
int enabled = 0;
|
|
int motorCanSetSpeed = 0;
|
|
|
|
// =========================================================================
|
|
|
|
pl_status = pC_->getIntegerParam(axisNo_, pC_->motorEnableRBV_, &enabled);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "enableMotorRBV_", axisNo_,
|
|
__PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
|
|
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution_,
|
|
&motorRecResolution);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorRecResolution_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
|
|
if (enabled == 0) {
|
|
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\", axis %d => %s, line %d:\nAxis is "
|
|
"disabled.\n",
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
|
return asynSuccess;
|
|
}
|
|
|
|
// Convert from EPICS to user / motor units
|
|
motorCoordinatesPosition = position * motorRecResolution;
|
|
motorVelocity = maxVelocity * motorRecResolution;
|
|
|
|
asynPrint(
|
|
pC_->pasynUserSelf, ASYN_TRACE_FLOW,
|
|
"Controller \"%s\", axis %d => %s, line %d:\nMove to position %lf.\n",
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, position);
|
|
|
|
// Check if the speed is allowed to be changed
|
|
pl_status = pC_->getIntegerParam(axisNo_, pC_->motorCanSetSpeed_,
|
|
&motorCanSetSpeed);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorCanSetSpeed_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
|
|
// Initialize the movement handshake
|
|
rw_status = pC_->write(axisNo_, 86, "0");
|
|
if (rw_status != asynSuccess) {
|
|
pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
return rw_status;
|
|
}
|
|
|
|
// Set the new motor speed, if the user is allowed to do so and if the
|
|
// motor speed changed since the last move command.
|
|
if (motorCanSetSpeed != 0 && lastSetSpeed_ != motorVelocity) {
|
|
|
|
lastSetSpeed_ = motorVelocity;
|
|
|
|
snprintf(value, sizeof(value), "%lf", motorVelocity);
|
|
rw_status = pC_->write(axisNo_, 05, value);
|
|
if (rw_status != asynSuccess) {
|
|
pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status,
|
|
"motorStatusProblem_", axisNo_,
|
|
__PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
return rw_status;
|
|
}
|
|
|
|
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW,
|
|
"Controller \"%s\", axis %d => %s, line %d:\nSetting speed "
|
|
"to %lf.\n",
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
motorVelocity);
|
|
}
|
|
|
|
// Set the target position
|
|
snprintf(value, sizeof(value), "%lf", motorCoordinatesPosition);
|
|
rw_status = pC_->write(axisNo_, 02, value);
|
|
if (rw_status != asynSuccess) {
|
|
pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
return rw_status;
|
|
}
|
|
|
|
// Start the move
|
|
if (relative) {
|
|
rw_status = pC_->write(axisNo_, 00, "2");
|
|
} else {
|
|
rw_status = pC_->write(axisNo_, 00, "1");
|
|
}
|
|
if (rw_status != asynSuccess) {
|
|
pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
return rw_status;
|
|
}
|
|
|
|
// In the next poll, we will check if the handshake has been performed in a
|
|
// reasonable time
|
|
waitForHandshake_ = true;
|
|
timeAtHandshake_ = time(NULL);
|
|
|
|
// Waiting for a handshake is already part of the movement procedure =>
|
|
// Start the watchdog
|
|
if (startMovTimeoutWatchdog() != asynSuccess) {
|
|
return asynError;
|
|
}
|
|
|
|
return rw_status;
|
|
}
|
|
|
|
asynStatus masterMacsAxis::stop(double acceleration) {
|
|
|
|
// Status of read-write-operations of ASCII commands to the controller
|
|
asynStatus rw_status = asynSuccess;
|
|
|
|
// Status of parameter library operations
|
|
asynStatus pl_status = asynSuccess;
|
|
|
|
// =========================================================================
|
|
|
|
rw_status = pC_->write(axisNo_, 00, "8");
|
|
if (rw_status != asynSuccess) {
|
|
pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
}
|
|
|
|
return rw_status;
|
|
}
|
|
|
|
/*
|
|
Home the axis. On absolute encoder systems, this is a no-op
|
|
*/
|
|
asynStatus masterMacsAxis::doHome(double min_velocity, double max_velocity,
|
|
double acceleration, int forwards) {
|
|
|
|
// Status of read-write-operations of ASCII commands to the controller
|
|
asynStatus rw_status = asynSuccess;
|
|
|
|
// Status of parameter library operations
|
|
asynStatus pl_status = asynSuccess;
|
|
|
|
char response[pC_->MAXBUF_];
|
|
|
|
// =========================================================================
|
|
|
|
pl_status = pC_->getStringParam(axisNo_, pC_->encoderType_,
|
|
sizeof(response), response);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "encoderType_", axisNo_,
|
|
__PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
|
|
// Only send the home command if the axis has an incremental encoder
|
|
if (strcmp(response, IncrementalEncoder) == 0) {
|
|
|
|
// Initialize the movement handshake
|
|
rw_status = pC_->write(axisNo_, 86, "0");
|
|
if (rw_status != asynSuccess) {
|
|
pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status,
|
|
"motorStatusProblem_", axisNo_,
|
|
__PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
return rw_status;
|
|
}
|
|
|
|
rw_status = pC_->write(axisNo_, 00, "9");
|
|
if (rw_status != asynSuccess) {
|
|
return rw_status;
|
|
}
|
|
|
|
// In the next poll, we will check if the handshake has been performed
|
|
// in a reasonable time
|
|
waitForHandshake_ = true;
|
|
timeAtHandshake_ = time(NULL);
|
|
return asynSuccess;
|
|
} else {
|
|
return asynError;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Read the encoder type and update the parameter library accordingly
|
|
*/
|
|
asynStatus masterMacsAxis::readEncoderType() {
|
|
|
|
// Status of read-write-operations of ASCII commands to the controller
|
|
asynStatus rw_status = asynSuccess;
|
|
|
|
// Status of parameter library operations
|
|
asynStatus pl_status = asynSuccess;
|
|
|
|
char command[pC_->MAXBUF_], response[pC_->MAXBUF_];
|
|
int nvals = 0;
|
|
int encoder_id = 0;
|
|
|
|
// =========================================================================
|
|
|
|
// Check if this is an absolute encoder
|
|
rw_status = pC_->read(axisNo_, 60, response);
|
|
if (rw_status != asynSuccess) {
|
|
return rw_status;
|
|
}
|
|
|
|
nvals = sscanf(response, "%d", &encoder_id);
|
|
if (nvals != 1) {
|
|
return pC_->errMsgCouldNotParseResponse(command, response, axisNo_,
|
|
__PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
|
|
/*
|
|
Defined encoder IDs:
|
|
0=INC (Incremental)
|
|
1=SSI (Absolute encoder with SSI interface)
|
|
2=SSI (Absolute encoder with BiSS interface)
|
|
*/
|
|
if (encoder_id == 0) {
|
|
pl_status = setStringParam(pC_->encoderType_, IncrementalEncoder);
|
|
} else {
|
|
pl_status = setStringParam(pC_->encoderType_, AbsoluteEncoder);
|
|
}
|
|
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "encoderType_", axisNo_,
|
|
__PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
return asynSuccess;
|
|
}
|
|
|
|
asynStatus masterMacsAxis::enable(bool on) {
|
|
|
|
int timeout_enable_disable = 2;
|
|
char value[pC_->MAXBUF_];
|
|
|
|
// Status of read-write-operations of ASCII commands to the controller
|
|
asynStatus rw_status = asynSuccess;
|
|
|
|
// Status of parameter library operations
|
|
asynStatus pl_status = asynSuccess;
|
|
|
|
bool moving = false;
|
|
|
|
// =========================================================================
|
|
|
|
doPoll(&moving);
|
|
|
|
// If the axis is currently moving, it cannot be disabled. Ignore the
|
|
// command and inform the user. We check the last known status of the
|
|
// axis instead of "moving", since status -6 is also moving, but the
|
|
// motor can actually be disabled in this state!
|
|
if (moving) {
|
|
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\", axis %d => %s, line %d:\nAxis is not "
|
|
"idle and can therefore not be disabled.\n",
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
|
|
|
pl_status =
|
|
setStringParam(pC_->motorMessageText_,
|
|
"Axis cannot be disabled while it is moving.");
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
|
|
return asynError;
|
|
}
|
|
|
|
// Axis is already enabled / disabled and a new enable / disable command
|
|
// was sent => Do nothing
|
|
|
|
if ((readyToBeSwitchedOn() && switchedOn()) == on) {
|
|
asynPrint(
|
|
pC_->pasynUserSelf, ASYN_TRACE_WARNING,
|
|
"Controller \"%s\", axis %d => %s, line %d:\nAxis is already %s.\n",
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
on ? "enabled" : "disabled");
|
|
return asynSuccess;
|
|
}
|
|
|
|
// Enable / disable the axis if it is not moving
|
|
snprintf(value, sizeof(value), "%d", on);
|
|
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW,
|
|
"Controller \"%s\", axis %d => %s, line %d:\n%s axis.\n",
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
on ? "Enable" : "Disable");
|
|
if (on == 0) {
|
|
pl_status = setStringParam(pC_->motorMessageText_, "Disabling ...");
|
|
} else {
|
|
pl_status = setStringParam(pC_->motorMessageText_, "Enabling ...");
|
|
}
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
rw_status = pC_->write(axisNo_, 04, value);
|
|
if (rw_status != asynSuccess) {
|
|
return rw_status;
|
|
}
|
|
|
|
// Query the axis status every few milliseconds until the axis has been
|
|
// enabled or until the timeout has been reached
|
|
int startTime = time(NULL);
|
|
while (time(NULL) < startTime + timeout_enable_disable) {
|
|
|
|
// Read the axis status
|
|
usleep(100000);
|
|
rw_status = readAxisStatus();
|
|
if (rw_status != asynSuccess) {
|
|
return rw_status;
|
|
}
|
|
|
|
if (switchedOn() == on) {
|
|
bool moving = false;
|
|
// Perform a poll to update the parameter library
|
|
poll(&moving);
|
|
return asynSuccess;
|
|
}
|
|
}
|
|
|
|
// Failed to change axis status within timeout_enable_disable => Send a
|
|
// corresponding message
|
|
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
"Controller \"%s\", axis %d => %s, line %d:\nFailed to %s axis "
|
|
"within %d seconds\n",
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
on ? "enable" : "disable", timeout_enable_disable);
|
|
|
|
// Output message to user
|
|
snprintf(value, sizeof(value), "Failed to %s within %d seconds",
|
|
on ? "enable" : "disable", timeout_enable_disable);
|
|
pl_status = setStringParam(pC_->motorMessageText_, "Enabling ...");
|
|
if (pl_status != asynSuccess) {
|
|
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
|
|
axisNo_, __PRETTY_FUNCTION__,
|
|
__LINE__);
|
|
}
|
|
return asynError;
|
|
}
|
|
|
|
/**
|
|
Convert a float to an unint16_t bitset
|
|
*/
|
|
std::bitset<16> toBitset(float val) {
|
|
|
|
// Convert the 32-bit float to an uint32_t. This works independently of the
|
|
// endianess of the system.
|
|
uint32_t temp = (uint32_t)val;
|
|
|
|
// Take the bottom half of bits to convert the 32 bit uint to a 16 bit uint
|
|
uint16_t lowerHalf = (uint16_t)(temp & 0xFFFF);
|
|
return std::bitset<16>(lowerHalf);
|
|
}
|
|
|
|
asynStatus masterMacsAxis::readAxisStatus() {
|
|
char response[pC_->MAXBUF_];
|
|
|
|
// =========================================================================
|
|
|
|
asynStatus rw_status = pC_->read(axisNo_, 10, response);
|
|
if (rw_status == asynSuccess) {
|
|
|
|
float axisStatus = 0;
|
|
int nvals = sscanf(response, "%f", &axisStatus);
|
|
if (nvals != 1) {
|
|
return pC_->errMsgCouldNotParseResponse(
|
|
"R10", response, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
|
|
axisStatus_ = toBitset(axisStatus);
|
|
}
|
|
|
|
return rw_status;
|
|
}
|
|
|
|
asynStatus masterMacsAxis::readAxisError() {
|
|
char response[pC_->MAXBUF_];
|
|
|
|
// =========================================================================
|
|
|
|
asynStatus rw_status = pC_->read(axisNo_, 11, response);
|
|
if (rw_status == asynSuccess) {
|
|
|
|
float axisError = 0;
|
|
int nvals = sscanf(response, "%f", &axisError);
|
|
if (nvals != 1) {
|
|
return pC_->errMsgCouldNotParseResponse(
|
|
"R11", response, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
|
}
|
|
axisError_ = toBitset(axisError);
|
|
}
|
|
return rw_status;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
/** The following functions are C-wrappers, and can be called directly from
|
|
* iocsh */
|
|
|
|
extern "C" {
|
|
|
|
/*
|
|
C wrapper for the axis constructor. Please refer to the masterMacsAxis
|
|
constructor documentation. The controller is read from the portName.
|
|
*/
|
|
asynStatus masterMacsCreateAxis(const char *portName, int axis) {
|
|
|
|
/*
|
|
findAsynPortDriver is a asyn library FFI function which uses the C ABI.
|
|
Therefore it returns a void pointer instead of e.g. a pointer to a
|
|
superclass of the controller such as asynPortDriver. Type-safe upcasting
|
|
via dynamic_cast is therefore not possible directly. However, we do know
|
|
that the void pointer is either a pointer to asynPortDriver (if a driver
|
|
with the specified name exists) or a nullptr. Therefore, we first do a
|
|
nullptr check, then a cast to asynPortDriver and lastly a (typesafe)
|
|
dynamic_upcast to Controller
|
|
*/
|
|
void *ptr = findAsynPortDriver(portName);
|
|
if (ptr == nullptr) {
|
|
/*
|
|
We can't use asynPrint here since this macro would require us
|
|
to get a lowLevelPortUser_ from a pointer to an asynPortDriver.
|
|
However, the given pointer is a nullptr and therefore doesn't
|
|
have a lowLevelPortUser_! 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
|
|
masterMacsController *pC = dynamic_cast<masterMacsController *>(apd);
|
|
if (pC == nullptr) {
|
|
errlogPrintf("Controller \"%s\" => %s, line %d:\nController is not a "
|
|
"masterMacsController.",
|
|
portName, __PRETTY_FUNCTION__, __LINE__);
|
|
return asynError;
|
|
}
|
|
|
|
// Prevent manipulation of the controller from other threads while we
|
|
// create the new axis.
|
|
pC->lock();
|
|
|
|
/*
|
|
We create a new instance of the axis, using the "new" keyword to
|
|
allocate it on the heap while avoiding RAII. The created object is
|
|
registered in EPICS in its constructor and can safely be "leaked" here.
|
|
*/
|
|
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
|
#pragma GCC diagnostic ignored "-Wunused-variable"
|
|
masterMacsAxis *pAxis = new masterMacsAxis(pC, axis);
|
|
|
|
// Allow manipulation of the controller again
|
|
pC->unlock();
|
|
return asynSuccess;
|
|
}
|
|
|
|
/*
|
|
This is boilerplate code which is used to make the FFI functions
|
|
CreateController and CreateAxis "known" to the IOC shell (iocsh).
|
|
*/
|
|
|
|
#ifdef vxWorks
|
|
#else
|
|
|
|
/*
|
|
Same procedure as for the CreateController function, but for the axis
|
|
itself.
|
|
*/
|
|
static const iocshArg CreateAxisArg0 = {"Controller name (e.g. mmacs1)",
|
|
iocshArgString};
|
|
static const iocshArg CreateAxisArg1 = {"Axis number", iocshArgInt};
|
|
static const iocshArg *const CreateAxisArgs[] = {&CreateAxisArg0,
|
|
&CreateAxisArg1};
|
|
static const iocshFuncDef configMasterMacsCreateAxis = {"masterMacsAxis", 2,
|
|
CreateAxisArgs};
|
|
static void configMasterMacsCreateAxisCallFunc(const iocshArgBuf *args) {
|
|
masterMacsCreateAxis(args[0].sval, args[1].ival);
|
|
}
|
|
|
|
// This function is made known to EPICS in masterMacs.dbd and is called by
|
|
// EPICS in order to register both functions in the IOC shell
|
|
static void masterMacsAxisRegister(void) {
|
|
iocshRegister(&configMasterMacsCreateAxis,
|
|
configMasterMacsCreateAxisCallFunc);
|
|
}
|
|
epicsExportRegistrar(masterMacsAxisRegister);
|
|
|
|
#endif
|
|
|
|
} // extern "C"
|