4 Commits

7 changed files with 502 additions and 422 deletions

View File

@ -6,6 +6,9 @@ BUILDCLASSES=Linux
EPICS_VERSIONS=7.0.7 EPICS_VERSIONS=7.0.7
ARCH_FILTER=RHEL% ARCH_FILTER=RHEL%
# Additional module dependencies
REQUIRED+=sinqMotor
# Specify the version of asynMotor we want to build against # Specify the version of asynMotor we want to build against
motorBase_VERSION=7.2.2 motorBase_VERSION=7.2.2
@ -28,4 +31,4 @@ TEMPLATES += sinqMotor/db/sinqMotor.db
DBDS += sinqMotor/src/sinqMotor.dbd DBDS += sinqMotor/src/sinqMotor.dbd
DBDS += src/masterMacs.dbd DBDS += src/masterMacs.dbd
USR_CFLAGS += -Wall -Wextra -Weffc++ -Wunused-result -Wextra -Werror USR_CFLAGS += -Wall -Wextra -Weffc++ -Wunused-result -Wpedantic -Wextra -Werror

View File

@ -57,8 +57,6 @@ setThresholdComTimeout("$(NAME)", 100, 1);
# Parametrize the EPICS record database with the substitution file named after the MCU. # Parametrize the EPICS record database with the substitution file named after the MCU.
epicsEnvSet("SINQDBPATH","$(masterMacs_DB)/sinqMotor.db") epicsEnvSet("SINQDBPATH","$(masterMacs_DB)/sinqMotor.db")
dbLoadTemplate("$(TOP)/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$(NAME)") dbLoadTemplate("$(TOP)/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$(NAME)")
epicsEnvSet("SINQDBPATH","$(masterMacs_DB)/masterMacs.db")
dbLoadTemplate("$(TOP)/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$(NAME)")
dbLoadRecords("$(masterMacs_DB)/asynRecord.db","P=$(INSTR)$(NAME),PORT=$(ASYN_PORT)") dbLoadRecords("$(masterMacs_DB)/asynRecord.db","P=$(INSTR)$(NAME),PORT=$(ASYN_PORT)")
``` ```
@ -68,4 +66,4 @@ Please see the documentation for the module sinqMotor: https://git.psi.ch/sinq-e
### How to build it ### How to build it
Please see the documentation for the module sinqMotor: https://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/main/README.md. This driver can be compiled and installed by running `make install` from the same directory where the Makefile is located. However, since it uses the git submodule sinqMotor, make sure that the correct version of the submodule repository is checked out AND the change is commited (`git status` shows no non-committed changes). Please see the section "Usage as static dependency" in https://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/main/README.md for more details.

View File

@ -11,21 +11,6 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
struct masterMacsAxisImpl {
/*
The axis status and axis error of MasterMACS are given as an integer from
the controller. The 16 individual bits contain the actual information.
*/
std::bitset<16> axisStatus;
std::bitset<16> axisError;
double lastSetSpeed;
bool waitForHandshake;
time_t timeAtHandshake;
bool needInit = true;
bool targetReachedUninitialized;
};
/* /*
A special communication timeout is used in the following two cases: A special communication timeout is used in the following two cases:
1) Enable command 1) Enable command
@ -75,9 +60,9 @@ void appendErrorMessage(char *fullMessage, size_t capacityFullMessage,
// fullMessage suffices. We need capacity for one additional character // fullMessage suffices. We need capacity for one additional character
// because of the linebreak. // because of the linebreak.
if (lenFullMessage + lenToBeAppended + 1 < capacityFullMessage) { if (lenFullMessage + lenToBeAppended + 1 < capacityFullMessage) {
// Append the linebreak and readd the null terminator behind it // Append the linebreak and set the null terminator behind it
// fullMessage[lenFullMessage] = '\n'; fullMessage[lenFullMessage] = '\n';
// fullMessage[lenFullMessage + 1] = '\0'; fullMessage[lenFullMessage + 1] = '\0';
// We check before that the capacity of fullMessage is sufficient // We check before that the capacity of fullMessage is sufficient
strcat(fullMessage, toBeAppended); strcat(fullMessage, toBeAppended);
@ -119,14 +104,17 @@ masterMacsAxis::masterMacsAxis(masterMacsController *pC, int axisNo)
// Collect all axes into this list which will be used in the hook function // Collect all axes into this list which will be used in the hook function
axes.push_back(this); axes.push_back(this);
pMasterMacsA_ = std::make_unique<masterMacsAxisImpl>((masterMacsAxisImpl){ // Initialize all member variables
.axisStatus = std::bitset<16>(0), axisStatus_ = std::bitset<16>(0);
.axisError = std::bitset<16>(0), axisError_ = std::bitset<16>(0);
.lastSetSpeed = 0.0,
.waitForHandshake = false, // Initial value for the motor speed, is overwritten in atFirstPoll.
.timeAtHandshake = 0, lastSetSpeed_ = 0.0;
.targetReachedUninitialized = true,
}); // Flag for handshake waiting
waitForHandshake_ = false;
timeAtHandshake_ = 0;
targetReachedUninitialized_ = true;
// masterMacs motors can always be disabled // masterMacs motors can always be disabled
status = pC_->setIntegerParam(axisNo_, pC_->motorCanDisable(), 1); status = pC_->setIntegerParam(axisNo_, pC_->motorCanDisable(), 1);
@ -150,20 +138,6 @@ masterMacsAxis::masterMacsAxis(masterMacsController *pC, int axisNo)
pC_->portName, axisNo, __PRETTY_FUNCTION__, __LINE__, pC_->portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
pC_->stringifyAsynStatus(status)); pC_->stringifyAsynStatus(status));
} }
// Even though this happens already in sinqAxis, a default value for
// motorMessageText is set here again, because apparently the sinqAxis
// constructor is not run before the string is accessed?
status = setStringParam(pC_->motorMessageText(), "");
if (status != asynSuccess) {
asynPrint(pC_->pasynUser(), 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);
}
} }
masterMacsAxis::~masterMacsAxis(void) { masterMacsAxis::~masterMacsAxis(void) {
@ -265,7 +239,7 @@ asynStatus masterMacsAxis::init() {
// Cache the motor speed. If this value differs from the one in the motor // 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 // record at the start of a movement, the motor record value is sent to
// MasterMACS. // MasterMACS.
pMasterMacsA_->lastSetSpeed = motorVelocity; lastSetSpeed_ = motorVelocity;
// Store the motor position in the parameter library // Store the motor position in the parameter library
pl_status = setMotorPosition(motorPosition); pl_status = setMotorPosition(motorPosition);
@ -301,10 +275,6 @@ asynStatus masterMacsAxis::init() {
pC_->stringifyAsynStatus(pl_status)); pC_->stringifyAsynStatus(pl_status));
return pl_status; return pl_status;
} }
// Axis is fully initialized
setNeedInit(false);
return pl_status; return pl_status;
} }
@ -335,21 +305,13 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
// ========================================================================= // =========================================================================
// Does the axis need to be intialized?
if (needInit()) {
rw_status = init();
if (rw_status != asynSuccess) {
return rw_status;
}
}
// Are we currently waiting for a handshake? // Are we currently waiting for a handshake?
if (pMasterMacsA_->waitForHandshake) { if (waitForHandshake_) {
// Check if the handshake takes too long and communicate an error in // Check if the handshake takes too long and communicate an error in
// this case. A handshake should not take more than 5 seconds. // this case. A handshake should not take more than 5 seconds.
time_t currentTime = time(NULL); time_t currentTime = time(NULL);
bool timedOut = (currentTime > pMasterMacsA_->timeAtHandshake + 5); bool timedOut = (currentTime > timeAtHandshake_ + 5);
if (pC_->getMsgPrintControl().shouldBePrinted( if (pC_->getMsgPrintControl().shouldBePrinted(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, timedOut, pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, timedOut,
@ -359,12 +321,18 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
"handshake at %ld s and didn't get a positive reply yet " "handshake at %ld s and didn't get a positive reply yet "
"(current time is %ld s).\n", "(current time is %ld s).\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pMasterMacsA_->timeAtHandshake, currentTime); timeAtHandshake_, currentTime);
} }
if (timedOut) { if (timedOut) {
setAxisParamChecked(this, motorMessageText, pl_status =
"Timed out while waiting for a handshake"); setStringParam(pC_->motorMessageText(),
"Timed out while waiting for a handshake");
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
} }
pC_->read(axisNo_, 86, response); pC_->read(axisNo_, 86, response);
@ -381,22 +349,39 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
if (handshakePerformed == 1.0) { if (handshakePerformed == 1.0) {
// Handshake has been performed successfully -> Continue with the // Handshake has been performed successfully -> Continue with the
// poll // poll
pMasterMacsA_->waitForHandshake = false; waitForHandshake_ = false;
pMasterMacsA_->targetReachedUninitialized = false; targetReachedUninitialized_ = false;
} else { } else {
// Still waiting for the handshake - try again in the next busy // Still waiting for the handshake - try again in the next busy
// poll. This is already part of the movement procedure. // poll. This is already part of the movement procedure.
*moving = true; *moving = true;
setAxisParamChecked(this, motorStatusMoving, *moving); pl_status = setIntegerParam(pC_->motorStatusMoving(), *moving);
setAxisParamChecked(this, motorStatusDone, !(*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; return asynSuccess;
} }
} }
// Motor resolution from parameter library // Motor resolution from parameter library
getAxisParamChecked(this, motorRecResolution, &motorRecResolution); 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 // Read the previous motor position
pl_status = motorPosition(&previousPosition); pl_status = motorPosition(&previousPosition);
@ -410,7 +395,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
return rw_status; return rw_status;
} }
if (pMasterMacsA_->targetReachedUninitialized) { if (targetReachedUninitialized_) {
*moving = false; *moving = false;
} else { } else {
if (targetReached() || !switchedOn()) { if (targetReached() || !switchedOn()) {
@ -421,7 +406,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
} }
if (targetReached()) { if (targetReached()) {
pMasterMacsA_->targetReachedUninitialized = false; targetReachedUninitialized_ = false;
} }
// Read the current position // Read the current position
@ -460,16 +445,22 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
pC_->getMsgPrintControl().getSuffix()); pC_->getMsgPrintControl().getSuffix());
} }
setAxisParamChecked(this, motorMessageText, pl_status =
"Communication error between PC and motor " setStringParam(pC_->motorMessageText(),
"controller. Please call the support."); "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; poll_status = asynError;
} else { } else {
// This buffer must be initialized to zero because we build the // This buffer must be initialized to zero because we build the
// error message by appending strings. // error message by appending strings.
char errorMessage[pC_->MAXBUF_] = {0}; char userMessage[pC_->MAXBUF_] = {0};
char shellMessage[pC_->MAXBUF_] = {0}; char shellMessage[pC_->MAXBUF_] = {0};
// Concatenate all other errors // Concatenate all other errors
@ -477,7 +468,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
appendErrorMessage(shellMessage, sizeof(shellMessage), appendErrorMessage(shellMessage, sizeof(shellMessage),
"Short circuit fault."); "Short circuit fault.");
appendErrorMessage( appendErrorMessage(
errorMessage, sizeof(errorMessage), userMessage, sizeof(userMessage),
"Short circuit error. Please call the support."); "Short circuit error. Please call the support.");
poll_status = asynError; poll_status = asynError;
@ -486,7 +477,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
if (encoderError()) { if (encoderError()) {
appendErrorMessage(shellMessage, sizeof(shellMessage), appendErrorMessage(shellMessage, sizeof(shellMessage),
"Encoder error."); "Encoder error.");
appendErrorMessage(errorMessage, sizeof(errorMessage), appendErrorMessage(userMessage, sizeof(userMessage),
"Encoder error. Please call the support."); "Encoder error. Please call the support.");
poll_status = asynError; poll_status = asynError;
@ -497,7 +488,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
shellMessage, sizeof(shellMessage), shellMessage, sizeof(shellMessage),
"Maximum callowed following error exceeded."); "Maximum callowed following error exceeded.");
appendErrorMessage( appendErrorMessage(
errorMessage, sizeof(errorMessage), userMessage, sizeof(userMessage),
"Maximum allowed following error exceeded.Check if " "Maximum allowed following error exceeded.Check if "
"movement range is blocked. Otherwise please call the " "movement range is blocked. Otherwise please call the "
"support."); "support.");
@ -508,7 +499,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
if (feedbackError()) { if (feedbackError()) {
appendErrorMessage(shellMessage, sizeof(shellMessage), appendErrorMessage(shellMessage, sizeof(shellMessage),
"Feedback error."); "Feedback error.");
appendErrorMessage(errorMessage, sizeof(errorMessage), appendErrorMessage(userMessage, sizeof(userMessage),
"Feedback error. Please call the support."); "Feedback error. Please call the support.");
poll_status = asynError; poll_status = asynError;
@ -544,7 +535,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
// Generic error message for user // Generic error message for user
appendErrorMessage( appendErrorMessage(
errorMessage, sizeof(errorMessage), userMessage, sizeof(userMessage),
"Software limits or end switch hit. Try homing the motor, " "Software limits or end switch hit. Try homing the motor, "
"moving in the opposite direction or check the SPS for " "moving in the opposite direction or check the SPS for "
"errors (if available). Otherwise please call the " "errors (if available). Otherwise please call the "
@ -557,7 +548,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
appendErrorMessage(shellMessage, sizeof(shellMessage), appendErrorMessage(shellMessage, sizeof(shellMessage),
"Overcurrent error."); "Overcurrent error.");
appendErrorMessage( appendErrorMessage(
errorMessage, sizeof(errorMessage), userMessage, sizeof(userMessage),
"Overcurrent error. Please call the support."); "Overcurrent error. Please call the support.");
poll_status = asynError; poll_status = asynError;
@ -567,7 +558,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
appendErrorMessage(shellMessage, sizeof(shellMessage), appendErrorMessage(shellMessage, sizeof(shellMessage),
"Overtemperature error."); "Overtemperature error.");
appendErrorMessage( appendErrorMessage(
errorMessage, sizeof(errorMessage), userMessage, sizeof(userMessage),
"Overtemperature error. Please call the support."); "Overtemperature error. Please call the support.");
poll_status = asynError; poll_status = asynError;
@ -577,7 +568,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
appendErrorMessage(shellMessage, sizeof(shellMessage), appendErrorMessage(shellMessage, sizeof(shellMessage),
"Overvoltage error."); "Overvoltage error.");
appendErrorMessage( appendErrorMessage(
errorMessage, sizeof(errorMessage), userMessage, sizeof(userMessage),
"Overvoltage error. Please call the support."); "Overvoltage error. Please call the support.");
poll_status = asynError; poll_status = asynError;
@ -587,7 +578,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
appendErrorMessage(shellMessage, sizeof(shellMessage), appendErrorMessage(shellMessage, sizeof(shellMessage),
"Undervoltage error."); "Undervoltage error.");
appendErrorMessage( appendErrorMessage(
errorMessage, sizeof(errorMessage), userMessage, sizeof(userMessage),
"Undervoltage error. Please call the support."); "Undervoltage error. Please call the support.");
poll_status = asynError; poll_status = asynError;
@ -596,7 +587,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
if (stoFault()) { if (stoFault()) {
appendErrorMessage(shellMessage, sizeof(shellMessage), appendErrorMessage(shellMessage, sizeof(shellMessage),
"STO input is on disable state."); "STO input is on disable state.");
appendErrorMessage(errorMessage, sizeof(errorMessage), appendErrorMessage(userMessage, sizeof(userMessage),
"STO fault. Please call the support."); "STO fault. Please call the support.");
poll_status = asynError; poll_status = asynError;
@ -614,7 +605,12 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
} }
} }
setAxisParamChecked(this, motorMessageText, errorMessage); pl_status = setStringParam(pC_->motorMessageText(), userMessage);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
} }
} else { } else {
pC_->getMsgPrintControl().resetCount(keyError, pC_->pasynUser()); pC_->getMsgPrintControl().resetCount(keyError, pC_->pasynUser());
@ -655,18 +651,40 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
directly, but need to shrink them a bit. In this case, we're shrinking 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. them by 0.1 mm or 0.1 degree (depending on the axis type) on both sides.
*/ */
getAxisParamChecked(this, motorLimitsOffset, &limitsOffset); 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; highLimit = highLimit - limitsOffset;
lowLimit = lowLimit + limitsOffset; lowLimit = lowLimit + limitsOffset;
setAxisParamChecked(this, motorHighLimitFromDriver, highLimit); pl_status = pC_->setDoubleParam(
setAxisParamChecked(this, motorLowLimitFromDriver, lowLimit); 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 // Update the enable PV
setAxisParamChecked(this, motorEnableRBV, pl_status = setIntegerParam(pC_->motorEnableRBV(),
readyToBeSwitchedOn() && switchedOn()); readyToBeSwitchedOn() && switchedOn());
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorEnableRBV_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
if (*moving) { if (*moving) {
// If the axis is moving, evaluate the movement direction // If the axis is moving, evaluate the movement direction
@ -679,11 +697,32 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
// Update the parameter library // Update the parameter library
if (hasError) { if (hasError) {
setAxisParamChecked(this, motorStatusProblem, true); 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__);
} }
setAxisParamChecked(this, motorStatusMoving, *moving);
setAxisParamChecked(this, motorStatusDone, !(*moving));
setAxisParamChecked(this, motorStatusDirection, direction);
pl_status = setMotorPosition(currentPosition); pl_status = setMotorPosition(currentPosition);
if (pl_status != asynSuccess) { if (pl_status != asynSuccess) {
@ -698,7 +737,10 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
double acceleration) { double acceleration) {
// Status of read-write-operations of ASCII commands to the controller // Status of read-write-operations of ASCII commands to the controller
asynStatus status = asynSuccess; asynStatus rw_status = asynSuccess;
// Status of parameter library operations
asynStatus pl_status = asynSuccess;
char value[pC_->MAXBUF_]; char value[pC_->MAXBUF_];
double motorCoordinatesPosition = 0.0; double motorCoordinatesPosition = 0.0;
@ -709,8 +751,19 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
// ========================================================================= // =========================================================================
getAxisParamChecked(this, motorEnableRBV, &enabled); pl_status = pC_->getIntegerParam(axisNo_, pC_->motorEnableRBV(), &enabled);
getAxisParamChecked(this, motorRecResolution, &motorRecResolution); 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) { if (enabled == 0) {
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR, asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
@ -730,26 +783,42 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, position); pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, position);
// Check if the speed is allowed to be changed // Check if the speed is allowed to be changed
getAxisParamChecked(this, motorCanSetSpeed, &motorCanSetSpeed); 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 // Initialize the movement handshake
status = pC_->write(axisNo_, 86, "0"); rw_status = pC_->write(axisNo_, 86, "0");
if (status != asynSuccess) { if (rw_status != asynSuccess) {
setAxisParamChecked(this, motorStatusProblem, true); pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
return status; 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 // Set the new motor speed, if the user is allowed to do so and if the
// motor speed changed since the last move command. // motor speed changed since the last move command.
if (motorCanSetSpeed != 0 && pMasterMacsA_->lastSetSpeed != motorVelocity) { if (motorCanSetSpeed != 0 && lastSetSpeed_ != motorVelocity) {
pMasterMacsA_->lastSetSpeed = motorVelocity; lastSetSpeed_ = motorVelocity;
snprintf(value, sizeof(value), "%lf", motorVelocity); snprintf(value, sizeof(value), "%lf", motorVelocity);
status = pC_->write(axisNo_, 05, value); rw_status = pC_->write(axisNo_, 05, value);
if (status != asynSuccess) { if (rw_status != asynSuccess) {
setAxisParamChecked(this, motorStatusProblem, true); pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
return status; if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status,
"motorStatusProblem_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
return rw_status;
} }
asynPrint(pC_->pasynUser(), ASYN_TRACE_FLOW, asynPrint(pC_->pasynUser(), ASYN_TRACE_FLOW,
@ -761,34 +830,43 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
// Set the target position // Set the target position
snprintf(value, sizeof(value), "%lf", motorCoordinatesPosition); snprintf(value, sizeof(value), "%lf", motorCoordinatesPosition);
status = pC_->write(axisNo_, 02, value); rw_status = pC_->write(axisNo_, 02, value);
if (status != asynSuccess) { if (rw_status != asynSuccess) {
setAxisParamChecked(this, motorStatusProblem, true); pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
return status; if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
return rw_status;
} }
// If the motor has just been enabled, use Enable // If the motor has just been enabled, use Enable
double timeout = pC_->comTimeout(); double timeout = pC_->comTimeout();
if (pMasterMacsA_->targetReachedUninitialized && if (targetReachedUninitialized_ && timeout < PowerCycleTimeout) {
timeout < PowerCycleTimeout) {
timeout = PowerCycleTimeout; timeout = PowerCycleTimeout;
} }
// Start the move // Start the move
if (relative) { if (relative) {
status = pC_->write(axisNo_, 00, "2", timeout); rw_status = pC_->write(axisNo_, 00, "2", timeout);
} else { } else {
status = pC_->write(axisNo_, 00, "1", timeout); rw_status = pC_->write(axisNo_, 00, "1", timeout);
} }
if (status != asynSuccess) { if (rw_status != asynSuccess) {
setAxisParamChecked(this, motorStatusProblem, true); pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
return status; 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 // In the next poll, we will check if the handshake has been performed in a
// reasonable time // reasonable time
pMasterMacsA_->waitForHandshake = true; waitForHandshake_ = true;
pMasterMacsA_->timeAtHandshake = time(NULL); timeAtHandshake_ = time(NULL);
// Waiting for a handshake is already part of the movement procedure => // Waiting for a handshake is already part of the movement procedure =>
// Start the watchdog // Start the watchdog
@ -796,35 +874,62 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
return asynError; return asynError;
} }
return status; return rw_status;
} }
asynStatus masterMacsAxis::stop(double acceleration) { asynStatus masterMacsAxis::stop(double acceleration) {
asynStatus status = pC_->write(axisNo_, 00, "8"); // Status of read-write-operations of ASCII commands to the controller
if (status != asynSuccess) { asynStatus rw_status = asynSuccess;
setAxisParamChecked(this, motorStatusProblem, true);
// 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__);
}
} }
// Reset the driver to idle state and move out of the handshake wait loop, return rw_status;
// if we're currently inside it.
pMasterMacsA_->waitForHandshake = false;
return status;
} }
asynStatus masterMacsAxis::doReset() { asynStatus masterMacsAxis::doReset() {
// Status of read-write-operations of ASCII commands to the controller
asynStatus rw_status = asynSuccess;
asynStatus status = pC_->write(axisNo_, 17, ""); // Status of parameter library operations
if (status != asynSuccess) { asynStatus pl_status = asynSuccess;
setAxisParamChecked(this, motorStatusProblem, true);
// =========================================================================
rw_status = pC_->write(axisNo_, 17, "");
if (rw_status != asynSuccess) {
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
} }
// Reset the driver to idle state and move out of the handshake wait loop, rw_status = pC_->write(axisNo_, 85, "");
// if we're currently inside it. if (rw_status != asynSuccess) {
pMasterMacsA_->waitForHandshake = false; pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
}
return status; return rw_status;
} }
/* /*
@ -833,31 +938,47 @@ Home the axis. On absolute encoder systems, this is a no-op
asynStatus masterMacsAxis::doHome(double min_velocity, double max_velocity, asynStatus masterMacsAxis::doHome(double min_velocity, double max_velocity,
double acceleration, int forwards) { 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_] = {0}; char response[pC_->MAXBUF_] = {0};
// ========================================================================= // =========================================================================
getAxisParamChecked(this, encoderType, &response); 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 // Only send the home command if the axis has an incremental encoder
if (strcmp(response, IncrementalEncoder) == 0) { if (strcmp(response, IncrementalEncoder) == 0) {
// Initialize the movement handshake // Initialize the movement handshake
asynStatus status = pC_->write(axisNo_, 86, "0"); rw_status = pC_->write(axisNo_, 86, "0");
if (status != asynSuccess) { if (rw_status != asynSuccess) {
setAxisParamChecked(this, motorStatusProblem, true); pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
return status; if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status,
"motorStatusProblem_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
return rw_status;
} }
status = pC_->write(axisNo_, 00, "9"); rw_status = pC_->write(axisNo_, 00, "9");
if (status != asynSuccess) { if (rw_status != asynSuccess) {
return status; return rw_status;
} }
// In the next poll, we will check if the handshake has been performed // In the next poll, we will check if the handshake has been performed
// in a reasonable time // in a reasonable time
pMasterMacsA_->waitForHandshake = true; waitForHandshake_ = true;
pMasterMacsA_->timeAtHandshake = time(NULL); timeAtHandshake_ = time(NULL);
return asynSuccess; return asynSuccess;
} else { } else {
return asynError; return asynError;
@ -869,6 +990,12 @@ Read the encoder type and update the parameter library accordingly
*/ */
asynStatus masterMacsAxis::readEncoderType() { 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_] = {0}; char command[pC_->MAXBUF_] = {0};
char response[pC_->MAXBUF_] = {0}; char response[pC_->MAXBUF_] = {0};
int nvals = 0; int nvals = 0;
@ -877,9 +1004,9 @@ asynStatus masterMacsAxis::readEncoderType() {
// ========================================================================= // =========================================================================
// Check if this is an absolute encoder // Check if this is an absolute encoder
asynStatus status = pC_->read(axisNo_, 60, response); rw_status = pC_->read(axisNo_, 60, response);
if (status != asynSuccess) { if (rw_status != asynSuccess) {
return status; return rw_status;
} }
nvals = sscanf(response, "%d", &encoder_id); nvals = sscanf(response, "%d", &encoder_id);
@ -895,18 +1022,28 @@ asynStatus masterMacsAxis::readEncoderType() {
2=SSI (Absolute encoder with BiSS interface) 2=SSI (Absolute encoder with BiSS interface)
*/ */
if (encoder_id == 0) { if (encoder_id == 0) {
setAxisParamChecked(this, encoderType, IncrementalEncoder); pl_status = setStringParam(pC_->encoderType(), IncrementalEncoder);
} else { } else {
setAxisParamChecked(this, encoderType, AbsoluteEncoder); pl_status = setStringParam(pC_->encoderType(), AbsoluteEncoder);
} }
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "encoderType_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
return asynSuccess; return asynSuccess;
} }
asynStatus masterMacsAxis::enable(bool on) { asynStatus masterMacsAxis::enable(bool on) {
int timeout_enable_disable = 2; int timeout_enable_disable = 2;
char msg[pC_->MAXBUF_]; 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;
// ========================================================================= // =========================================================================
@ -915,7 +1052,7 @@ asynStatus masterMacsAxis::enable(bool on) {
0. In order to prevent the poll method in interpreting the motor state as 0. In order to prevent the poll method in interpreting the motor state as
"moving", this flag is used. It is reset in the handshake. "moving", this flag is used. It is reset in the handshake.
*/ */
pMasterMacsA_->targetReachedUninitialized = true; targetReachedUninitialized_ = true;
/* /*
Continue regardless of the status returned by the poll; we just want to Continue regardless of the status returned by the poll; we just want to
@ -936,8 +1073,14 @@ asynStatus masterMacsAxis::enable(bool on) {
"idle and can therefore not be disabled.\n", "idle and can therefore not be disabled.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
setAxisParamChecked(this, motorMessageText, pl_status =
"Axis cannot be disabled while it is moving."); 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; return asynError;
} }
@ -955,24 +1098,33 @@ asynStatus masterMacsAxis::enable(bool on) {
} }
// Enable / disable the axis if it is not moving // Enable / disable the axis if it is not moving
snprintf(msg, sizeof(msg), "%d", on); snprintf(value, sizeof(value), "%d", on);
asynPrint(pC_->pasynUser(), ASYN_TRACE_FLOW, asynPrint(pC_->pasynUser(), ASYN_TRACE_FLOW,
"Controller \"%s\", axis %d => %s, line %d:\n%s axis.\n", "Controller \"%s\", axis %d => %s, line %d:\n%s axis.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
on ? "Enable" : "Disable"); 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__);
}
// The answer to the enable command on MasterMACS might take some time, // The answer to the enable command on MasterMACS might take some time,
// hence we wait for a custom timespan in seconds instead of // hence we wait for a custom timespan in seconds instead of
// pC_->comTimeout_ // pC_->comTimeout_
double timeout = pC_->comTimeout(); double timeout = pC_->comTimeout();
if (pMasterMacsA_->targetReachedUninitialized && if (targetReachedUninitialized_ && timeout < PowerCycleTimeout) {
timeout < PowerCycleTimeout) {
timeout = PowerCycleTimeout; timeout = PowerCycleTimeout;
} }
asynStatus status = pC_->write(axisNo_, 04, msg, timeout); rw_status = pC_->write(axisNo_, 04, value, timeout);
if (status != asynSuccess) { if (rw_status != asynSuccess) {
return status; return rw_status;
} }
// Query the axis status every few milliseconds until the axis has been // Query the axis status every few milliseconds until the axis has been
@ -982,9 +1134,9 @@ asynStatus masterMacsAxis::enable(bool on) {
// Read the axis status // Read the axis status
usleep(100000); usleep(100000);
status = readAxisStatus(); rw_status = readAxisStatus();
if (status != asynSuccess) { if (rw_status != asynSuccess) {
return status; return rw_status;
} }
if (switchedOn() == on) { if (switchedOn() == on) {
@ -1004,24 +1156,17 @@ asynStatus masterMacsAxis::enable(bool on) {
on ? "enable" : "disable", timeout_enable_disable); on ? "enable" : "disable", timeout_enable_disable);
// Output message to user // Output message to user
snprintf(msg, sizeof(msg), "Failed to %s within %d seconds", snprintf(value, sizeof(value), "Failed to %s within %d seconds",
on ? "enable" : "disable", timeout_enable_disable); on ? "enable" : "disable", timeout_enable_disable);
pl_status = setStringParam(pC_->motorMessageText(), "Enabling ...");
setAxisParamChecked(this, motorMessageText, msg); if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
return asynError; return asynError;
} }
bool masterMacsAxis::needInit() { return pMasterMacsA_->needInit; }
/**
* @brief Instruct the axis to run its init() function during the next poll
*
* @param needInit
*/
void masterMacsAxis::setNeedInit(bool needInit) {
pMasterMacsA_->needInit = needInit;
}
/** /**
Convert a float to an unint16_t bitset Convert a float to an unint16_t bitset
*/ */
@ -1051,7 +1196,7 @@ asynStatus masterMacsAxis::readAxisStatus() {
__PRETTY_FUNCTION__, __LINE__); __PRETTY_FUNCTION__, __LINE__);
} }
pMasterMacsA_->axisStatus = toBitset(axisStatus); axisStatus_ = toBitset(axisStatus);
} }
return rw_status; return rw_status;
@ -1071,83 +1216,11 @@ asynStatus masterMacsAxis::readAxisError() {
return pC_->couldNotParseResponse("R11", response, axisNo_, return pC_->couldNotParseResponse("R11", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__); __PRETTY_FUNCTION__, __LINE__);
} }
pMasterMacsA_->axisError = toBitset(axisError); axisError_ = toBitset(axisError);
} }
return rw_status; return rw_status;
} }
bool masterMacsAxis::readyToBeSwitchedOn() {
return pMasterMacsA_->axisStatus[0];
}
bool masterMacsAxis::switchedOn() { return pMasterMacsA_->axisStatus[1]; }
bool masterMacsAxis::faultConditionSet() {
return pMasterMacsA_->axisStatus[3];
}
bool masterMacsAxis::voltagePresent() { return pMasterMacsA_->axisStatus[4]; }
bool masterMacsAxis::quickStopping() {
return pMasterMacsA_->axisStatus[5] == 0;
}
bool masterMacsAxis::switchOnDisabled() { return pMasterMacsA_->axisStatus[6]; }
bool masterMacsAxis::warning() { return pMasterMacsA_->axisStatus[7]; }
bool masterMacsAxis::remoteMode() { return pMasterMacsA_->axisStatus[9]; }
bool masterMacsAxis::targetReached() { return pMasterMacsA_->axisStatus[10]; }
bool masterMacsAxis::internalLimitActive() {
return pMasterMacsA_->axisStatus[11];
}
bool masterMacsAxis::setEventHasOcurred() {
return pMasterMacsA_->axisStatus[14];
}
bool masterMacsAxis::powerEnabled() { return pMasterMacsA_->axisStatus[15]; }
bool masterMacsAxis::shortCircuit() { return pMasterMacsA_->axisError[1]; }
bool masterMacsAxis::encoderError() { return pMasterMacsA_->axisError[2]; }
bool masterMacsAxis::followingError() { return pMasterMacsA_->axisError[3]; }
bool masterMacsAxis::communicationError() {
return pMasterMacsA_->axisError[4];
}
bool masterMacsAxis::feedbackError() { return pMasterMacsA_->axisError[5]; }
bool masterMacsAxis::positiveLimitSwitch() {
return pMasterMacsA_->axisError[6];
}
bool masterMacsAxis::negativeLimitSwitch() {
return pMasterMacsA_->axisError[7];
}
bool masterMacsAxis::positiveSoftwareLimit() {
return pMasterMacsA_->axisError[8];
}
bool masterMacsAxis::negativeSoftwareLimit() {
return pMasterMacsA_->axisError[9];
}
bool masterMacsAxis::overCurrent() { return pMasterMacsA_->axisError[10]; }
bool masterMacsAxis::overTemperature() { return pMasterMacsA_->axisError[11]; }
bool masterMacsAxis::overVoltage() { return pMasterMacsA_->axisError[12]; }
bool masterMacsAxis::underVoltage() { return pMasterMacsA_->axisError[13]; }
bool masterMacsAxis::stoFault() { return pMasterMacsA_->axisError[15]; }
/***************************************************************************/ /***************************************************************************/
/** The following functions are C-wrappers, and can be called directly from /** The following functions are C-wrappers, and can be called directly from
* iocsh */ * iocsh */

View File

@ -1,10 +1,12 @@
#ifndef masterMacsAXIS_H #ifndef masterMacsAXIS_H
#define masterMacsAXIS_H #define masterMacsAXIS_H
#include "masterMacsController.h"
#include "sinqAxis.h" #include "sinqAxis.h"
#include <memory> #include <bitset>
struct masterMacsAxisImpl; // Forward declaration of the controller class to resolve the cyclic dependency
// between the controller and the axis .h-file. See
// https://en.cppreference.com/w/cpp/language/class.
class masterMacsController;
class masterMacsAxis : public sinqAxis { class masterMacsAxis : public sinqAxis {
public: public:
@ -103,25 +105,22 @@ class masterMacsAxis : public sinqAxis {
*/ */
asynStatus readEncoderType(); asynStatus readEncoderType();
/** protected:
* @brief Check if the axis needs to run its initialization function masterMacsController *pC_;
* double lastSetSpeed_;
* @return true bool waitForHandshake_;
* @return false time_t timeAtHandshake_;
*/
bool needInit();
/** bool targetReachedUninitialized_;
* @brief Instruct the axis to run its init() function during the next poll
*
* @param needInit
*/
void setNeedInit(bool needInit);
/** asynStatus readConfig();
* @brief Return a pointer to the axis controller
*/ /*
virtual masterMacsController *pController() override { return pC_; }; The axis status and axis error of MasterMACS are given as an integer from
the controller. The 16 individual bits contain the actual information.
*/
std::bitset<16> axisStatus_;
std::bitset<16> axisError_;
/** /**
* @brief Read the Master MACS status with the xR10 command and store the * @brief Read the Master MACS status with the xR10 command and store the
@ -131,7 +130,7 @@ class masterMacsAxis : public sinqAxis {
asynStatus readAxisStatus(); asynStatus readAxisStatus();
/* /*
The functions below read the specified status bit from the axisStatus The functions below read the specified status bit from the axisStatus_
bitset. Since a bit can either be 0 or 1, the return value is given as a bitset. Since a bit can either be 0 or 1, the return value is given as a
boolean. boolean.
*/ */
@ -139,68 +138,68 @@ class masterMacsAxis : public sinqAxis {
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus_
*/ */
bool readyToBeSwitchedOn(); bool readyToBeSwitchedOn() { return axisStatus_[0]; }
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus_
*/ */
bool switchedOn(); bool switchedOn() { return axisStatus_[1]; }
// Bit 2 is unused // Bit 2 is unused
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus_
*/ */
bool faultConditionSet(); bool faultConditionSet() { return axisStatus_[3]; }
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus_
*/ */
bool voltagePresent(); bool voltagePresent() { return axisStatus_[4]; }
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus_
*/ */
bool quickStopping(); bool quickStopping() { return axisStatus_[5] == 0; }
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus_
*/ */
bool switchOnDisabled(); bool switchOnDisabled() { return axisStatus_[6]; }
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus_
*/ */
bool warning(); bool warning() { return axisStatus_[7]; }
// Bit 8 is unused // Bit 8 is unused
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus_
*/ */
bool remoteMode(); bool remoteMode() { return axisStatus_[9]; }
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus_
*/ */
bool targetReached(); bool targetReached() { return axisStatus_[10]; }
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus_
*/ */
bool internalLimitActive(); bool internalLimitActive() { return axisStatus_[11]; }
// Bits 12 and 13 are unused // Bits 12 and 13 are unused
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus_
*/ */
bool setEventHasOcurred(); bool setEventHasOcurred() { return axisStatus_[14]; }
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus_
*/ */
bool powerEnabled(); bool powerEnabled() { return axisStatus_[15]; }
/** /**
* @brief Read the Master MACS status with the xR10 command and store the * @brief Read the Master MACS status with the xR10 command and store the
@ -218,76 +217,72 @@ class masterMacsAxis : public sinqAxis {
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError_
*/ */
bool shortCircuit(); bool shortCircuit() { return axisError_[1]; }
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError_
*/ */
bool encoderError(); bool encoderError() { return axisError_[2]; }
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError_
*/ */
bool followingError(); bool followingError() { return axisError_[3]; }
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError_
*/ */
bool communicationError(); bool communicationError() { return axisError_[4]; }
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError_
*/ */
bool feedbackError(); bool feedbackError() { return axisError_[5]; }
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError_
*/ */
bool positiveLimitSwitch(); bool positiveLimitSwitch() { return axisError_[6]; }
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError_
*/ */
bool negativeLimitSwitch(); bool negativeLimitSwitch() { return axisError_[7]; }
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError_
*/ */
bool positiveSoftwareLimit(); bool positiveSoftwareLimit() { return axisError_[8]; }
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError_
*/ */
bool negativeSoftwareLimit(); bool negativeSoftwareLimit() { return axisError_[9]; }
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError_
*/ */
bool overCurrent(); bool overCurrent() { return axisError_[10]; }
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError_
*/ */
bool overTemperature(); bool overTemperature() { return axisError_[11]; }
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError_
*/ */
bool overVoltage(); bool overVoltage() { return axisError_[12]; }
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError_
*/ */
bool underVoltage(); bool underVoltage() { return axisError_[13]; }
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError_
*/ */
bool stoFault(); bool stoFault() { return axisError_[15]; }
private:
masterMacsController *pC_;
std::unique_ptr<masterMacsAxisImpl> pMasterMacsA_;
}; };
#endif #endif

View File

@ -12,10 +12,6 @@
#include <string> #include <string>
#include <unistd.h> #include <unistd.h>
struct masterMacsControllerImpl {
double comTimeout;
};
/** /**
* @brief Copy src into dst and replace all NULL terminators up to the carriage * @brief Copy src into dst and replace all NULL terminators up to the carriage
* return with spaces. This allows to print *dst with asynPrint. * return with spaces. This allows to print *dst with asynPrint.
@ -63,10 +59,8 @@ masterMacsController::masterMacsController(const char *portName,
// Initialization of local variables // Initialization of local variables
asynStatus status = asynSuccess; asynStatus status = asynSuccess;
pMasterMacsC_ = // Initialization of all member variables
std::make_unique<masterMacsControllerImpl>((masterMacsControllerImpl){ comTimeout_ = comTimeout;
.comTimeout = comTimeout,
});
// ========================================================================= // =========================================================================
@ -136,6 +130,7 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
// Definition of local variables. // Definition of local variables.
asynStatus status = asynSuccess; asynStatus status = asynSuccess;
asynStatus pl_status = asynSuccess;
char fullCommand[MAXBUF_] = {0}; char fullCommand[MAXBUF_] = {0};
char fullResponse[MAXBUF_] = {0}; char fullResponse[MAXBUF_] = {0};
char drvMessageText[MAXBUF_] = {0}; char drvMessageText[MAXBUF_] = {0};
@ -163,7 +158,7 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
// Check if a custom timeout has been given // Check if a custom timeout has been given
if (comTimeout < 0.0) { if (comTimeout < 0.0) {
comTimeout = pMasterMacsC_->comTimeout; comTimeout = comTimeout_;
} }
masterMacsAxis *axis = getMasterMacsAxis(axisNo); masterMacsAxis *axis = getMasterMacsAxis(axisNo);
@ -197,7 +192,7 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
msgPrintControlKey comKey = msgPrintControlKey comKey =
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__); msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
if (status != asynSuccess) { if (status != asynSuccess) {
if (getMsgPrintControl().shouldBePrinted(comKey, true, pasynUserSelf)) { if (msgPrintControl_.shouldBePrinted(comKey, true, pasynUserSelf)) {
char printableCommand[MAXBUF_] = {0}; char printableCommand[MAXBUF_] = {0};
adjustForPrint(printableCommand, fullCommand, MAXBUF_); adjustForPrint(printableCommand, fullCommand, MAXBUF_);
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
@ -207,7 +202,7 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
stringifyAsynStatus(status), printableCommand); stringifyAsynStatus(status), printableCommand);
} }
} else { } else {
getMsgPrintControl().resetCount(comKey, pasynUserSelf); msgPrintControl_.resetCount(comKey, pasynUserSelf);
} }
// Create custom error messages for different failure modes // Create custom error messages for different failure modes
@ -254,31 +249,48 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
// Log the overall status (communication successfull or not) // Log the overall status (communication successfull or not)
if (status == asynSuccess) { if (status == asynSuccess) {
setAxisParamChecked(axis, motorStatusCommsError, false); pl_status = axis->setIntegerParam(this->motorStatusCommsError_, 0);
if (pl_status != asynSuccess) {
return paramLibAccessFailed(pl_status, "motorStatusCommsError_",
axisNo, __PRETTY_FUNCTION__, __LINE__);
}
} else if (status == asynDisconnected) {
// Do nothing
} else { } else {
// Set the error status bits only if the axis is not disconnected
/* // Check if the axis already is in an error communication mode. If
Since the communication failed, there is the possibility that the // it is not, upstream the error. This is done to avoid "flooding"
controller is not connected at all to the network. In that case, we // the user with different error messages if more than one error
cannot be sure that the information read out in the init method of the // ocurred before an error-free communication
axis is still up-to-date the next time we get a connection. Therefore, pl_status =
an info flag is set which the axis object can use at the start of its getIntegerParam(axisNo, motorStatusProblem_, &motorStatusProblem);
poll method to try to initialize itself. if (pl_status != asynSuccess) {
*/ return paramLibAccessFailed(pl_status, "motorStatusProblem_",
axis->setNeedInit(true); axisNo, __PRETTY_FUNCTION__, __LINE__);
}
/*
Check if the axis already is in an error communication mode. If
it is not, upstream the error. This is done to avoid "flooding"
the user with different error messages if more than one error
ocurred before an error-free communication
*/
getAxisParamChecked(axis, motorStatusProblem, &motorStatusProblem);
if (motorStatusProblem == 0) { if (motorStatusProblem == 0) {
setAxisParamChecked(axis, motorMessageText, drvMessageText); pl_status = axis->setStringParam(motorMessageText_, drvMessageText);
setAxisParamChecked(axis, motorStatusProblem, true); if (pl_status != asynSuccess) {
setAxisParamChecked(axis, motorStatusCommsError, false); return paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo, __PRETTY_FUNCTION__,
__LINE__);
}
pl_status = axis->setIntegerParam(motorStatusProblem_, 1);
if (pl_status != asynSuccess) {
return paramLibAccessFailed(pl_status, "motorStatusProblem",
axisNo, __PRETTY_FUNCTION__,
__LINE__);
}
pl_status = axis->setIntegerParam(motorStatusProblem_, 1);
if (pl_status != asynSuccess) {
return paramLibAccessFailed(pl_status, "motorStatusCommsError_",
axisNo, __PRETTY_FUNCTION__,
__LINE__);
}
} }
} }
@ -298,7 +310,6 @@ asynStatus masterMacsController::parseResponse(
const char *fullCommand, const char *fullResponse, char *drvMessageText, const char *fullCommand, const char *fullResponse, char *drvMessageText,
int *valueStart, int *valueStop, int axisNo, int tcpCmd, bool isRead) { int *valueStart, int *valueStop, int axisNo, int tcpCmd, bool isRead) {
bool responseValid = false;
int responseStart = 0; int responseStart = 0;
asynStatus status = asynSuccess; asynStatus status = asynSuccess;
int prevConnected = 0; int prevConnected = 0;
@ -308,13 +319,12 @@ asynStatus masterMacsController::parseResponse(
msgPrintControlKey parseKey = msgPrintControlKey parseKey =
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__); msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
masterMacsAxis *axis = getMasterMacsAxis(axisNo);
if (axis == nullptr) {
return asynError;
}
// Was the motor previously connected? // Was the motor previously connected?
getAxisParamChecked(axis, motorConnected, &prevConnected); status = getIntegerParam(axisNo, motorConnected(), &prevConnected);
if (status != asynSuccess) {
return paramLibAccessFailed(status, "motorConnected", axisNo,
__PRETTY_FUNCTION__, __LINE__);
}
// We don't use strlen here since the C string terminator 0x00 // We don't use strlen here since the C string terminator 0x00
// occurs in the middle of the char array. // occurs in the middle of the char array.
@ -326,7 +336,6 @@ asynStatus masterMacsController::parseResponse(
} else if (fullResponse[i] == '\x06') { } else if (fullResponse[i] == '\x06') {
// ACK // ACK
*valueStop = i; *valueStop = i;
responseValid = true;
// Motor wasn't connected before -> Update the paramLib entry and PV // Motor wasn't connected before -> Update the paramLib entry and PV
// to show it is now connected. // to show it is now connected.
@ -338,7 +347,16 @@ asynStatus masterMacsController::parseResponse(
"connected.\n", "connected.\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__); portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
setAxisParamChecked(axis, motorConnected, true); masterMacsAxis *axis = getMasterMacsAxis(axisNo);
if (axis == nullptr) {
return asynError;
}
status = axis->setIntegerParam(motorConnected(), 1);
if (status != asynSuccess) {
return paramLibAccessFailed(status, "motorConnected",
axisNo, __PRETTY_FUNCTION__,
__LINE__);
}
status = callParamCallbacks(); status = callParamCallbacks();
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
@ -349,7 +367,51 @@ asynStatus masterMacsController::parseResponse(
} }
} }
break; msgPrintControl_.resetCount(parseKey, pasynUserSelf);
// Check if the response matches the expectations. Each response
// contains the string "axisNo R tcpCmd" (including the spaces)
char expectedResponseSubstring[MAXBUF_] = {0};
// The response does not contain a leading 0 if tcpCmd only has
// a single digit!
if (isRead) {
snprintf(expectedResponseSubstring, MAXBUF_ - 4, "%d R %d",
axisNo, tcpCmd);
} else {
snprintf(expectedResponseSubstring, MAXBUF_ - 4, "%d S %d",
axisNo, tcpCmd);
}
msgPrintControlKey responseMatchKey = msgPrintControlKey(
portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
if (strstr(&fullResponse[responseStart],
expectedResponseSubstring) == NULL) {
adjustForPrint(printableCommand, fullCommand, MAXBUF_);
adjustForPrint(printableResponse, fullResponse, MAXBUF_);
if (msgPrintControl_.shouldBePrinted(parseKey, true,
pasynUserSelf)) {
asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER,
"Controller \"%s\", axis %d => %s, line "
"%d:\nMismatched "
"response %s to command %s.%s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
printableResponse, printableCommand,
msgPrintControl_.getSuffix());
}
snprintf(
drvMessageText, MAXBUF_,
"Mismatched response %s to command %s. Please call the "
"support.",
printableResponse, printableCommand);
return asynError;
} else {
msgPrintControl_.resetCount(responseMatchKey, pasynUserSelf);
}
return asynSuccess;
} else if (fullResponse[i] == '\x15') { } else if (fullResponse[i] == '\x15') {
/* /*
NAK NAK
@ -366,7 +428,16 @@ asynStatus masterMacsController::parseResponse(
"disconnected.\n", "disconnected.\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__); portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
setAxisParamChecked(axis, motorConnected, false); masterMacsAxis *axis = getMasterMacsAxis(axisNo);
if (axis == nullptr) {
return asynError;
}
status = axis->setIntegerParam(motorConnected(), 0);
if (status != asynSuccess) {
return paramLibAccessFailed(status, "motorConnected",
axisNo, __PRETTY_FUNCTION__,
__LINE__);
}
status = callParamCallbacks(); status = callParamCallbacks();
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
@ -376,80 +447,33 @@ asynStatus masterMacsController::parseResponse(
return status; return status;
} }
} }
break; return asynDisconnected;
} else if (fullResponse[i] == '\x18') { } else if (fullResponse[i] == '\x18') {
// CAN // CAN
snprintf(drvMessageText, MAXBUF_, snprintf(drvMessageText, MAXBUF_,
"Tried to write with a read-only command. This is a " "Tried to write with a read-only command. This is a "
"bug, please call the support."); "bug, please call the support.");
if (getMsgPrintControl().shouldBePrinted(parseKey, true, if (msgPrintControl_.shouldBePrinted(parseKey, true,
pasynUserSelf)) { pasynUserSelf)) {
adjustForPrint(printableCommand, fullCommand, MAXBUF_); adjustForPrint(printableCommand, fullCommand, MAXBUF_);
asynPrint( asynPrint(
this->pasynUserSelf, ASYN_TRACE_ERROR, this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nTried to " "Controller \"%s\", axis %d => %s, line %d:\nTried to "
"write with the read-only command %s.%s\n", "write with the read-only command %s.%s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__, portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
printableCommand, getMsgPrintControl().getSuffix()); printableCommand, msgPrintControl_.getSuffix());
} }
responseValid = false;
break;
}
}
if (responseValid) {
getMsgPrintControl().resetCount(parseKey, pasynUserSelf);
// Check if the response matches the expectations. Each response
// contains the string "axisNo R tcpCmd" (including the spaces)
char expectedResponseSubstring[MAXBUF_] = {0};
// The response does not contain a leading 0 if tcpCmd only has
// a single digit!
if (isRead) {
snprintf(expectedResponseSubstring, MAXBUF_ - 4, "%d R %d", axisNo,
tcpCmd);
} else {
snprintf(expectedResponseSubstring, MAXBUF_ - 4, "%d S %d", axisNo,
tcpCmd);
}
msgPrintControlKey responseMatchKey =
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
if (strstr(&fullResponse[responseStart], expectedResponseSubstring) ==
NULL) {
adjustForPrint(printableCommand, fullCommand, MAXBUF_);
adjustForPrint(printableResponse, fullResponse, MAXBUF_);
if (getMsgPrintControl().shouldBePrinted(parseKey, true,
pasynUserSelf)) {
asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER,
"Controller \"%s\", axis %d => %s, line "
"%d:\nMismatched "
"response %s to command %s.%s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
printableResponse, printableCommand,
getMsgPrintControl().getSuffix());
}
snprintf(drvMessageText, MAXBUF_,
"Mismatched response %s to command %s. Please call the "
"support.",
printableResponse, printableCommand);
return asynError; return asynError;
} else {
getMsgPrintControl().resetCount(responseMatchKey, pasynUserSelf);
} }
} }
return asynSuccess; return asynError;
} }
asynStatus masterMacsController::readInt32(asynUser *pasynUser, asynStatus masterMacsController::readInt32(asynUser *pasynUser,
epicsInt32 *value) { epicsInt32 *value) {
// masterMacs can be disabled // masterMacs can be disabled
if (pasynUser->reason == motorCanDisable()) { if (pasynUser->reason == motorCanDisable_) {
*value = 1; *value = 1;
return asynSuccess; return asynSuccess;
} else { } else {
@ -457,8 +481,6 @@ asynStatus masterMacsController::readInt32(asynUser *pasynUser,
} }
} }
double masterMacsController::comTimeout() { return pMasterMacsC_->comTimeout; }
/***************************************************************************/ /***************************************************************************/
/** The following functions are C-wrappers, and can be called directly from /** The following functions are C-wrappers, and can be called directly from
* iocsh */ * iocsh */

View File

@ -8,16 +8,9 @@
#ifndef masterMacsController_H #ifndef masterMacsController_H
#define masterMacsController_H #define masterMacsController_H
#include "masterMacsAxis.h"
#include "sinqAxis.h" #include "sinqAxis.h"
#include "sinqController.h" #include "sinqController.h"
#include <memory>
// Forward declaration of the controller class to resolve the cyclic dependency
// between the controller and the axis .h-file. See
// https://en.cppreference.com/w/cpp/language/class.
class masterMacsAxis;
struct masterMacsControllerImpl;
class masterMacsController : public sinqController { class masterMacsController : public sinqController {
@ -38,15 +31,6 @@ class masterMacsController : public sinqController {
int numAxes, double movingPollPeriod, int numAxes, double movingPollPeriod,
double idlePollPeriod, double comTimeout); double idlePollPeriod, double comTimeout);
/**
* @brief Overloaded version of the sinqController version
*
* @param pasynUser
* @param value
* @return asynStatus
*/
asynStatus readInt32(asynUser *pasynUser, epicsInt32 *value);
/** /**
* @brief Get the axis object * @brief Get the axis object
* *
@ -139,10 +123,15 @@ class masterMacsController : public sinqController {
* *
* @return double Timeout in seconds * @return double Timeout in seconds
*/ */
double comTimeout(); double comTimeout() { return comTimeout_; }
asynStatus readInt32(asynUser *pasynUser, epicsInt32 *value);
private: private:
std::unique_ptr<masterMacsControllerImpl> pMasterMacsC_; /*
Stores the constructor input comTimeout
*/
double comTimeout_;
}; };
#endif /* masterMacsController_H */ #endif /* masterMacsController_H */