Compare commits

..

6 Commits

Author SHA1 Message Date
488d9c1279 Updated sinqMotor to new 1.6.0
Some checks failed
Test And Build / Lint (push) Failing after 4s
Test And Build / Build (push) Successful in 7s
2026-02-11 11:05:48 +01:00
41c1dad208 Motor now ignores limit switch hit errors when homing
Some checks failed
Test And Build / Lint (push) Failing after 3s
Test And Build / Build (push) Failing after 6s
See comment in src/masterMacsAxis.cpp, line 640ff.
2026-02-11 10:38:22 +01:00
4e3694977d Updated sinqMotor version 2026-02-11 10:38:15 +01:00
d24d2da50a Removed debug prints 2026-02-11 10:23:46 +01:00
516b8e7d68 added debug print 2026-02-11 10:19:44 +01:00
238a47f38e Ignore limit switch errors when the motor is homing or has been homed
Some checks failed
Test And Build / Lint (push) Failing after 4s
Test And Build / Build (push) Successful in 7s
2026-02-10 12:57:52 +01:00
3 changed files with 136 additions and 199 deletions

View File

@@ -9,6 +9,8 @@ ARCH_FILTER=RHEL%
# Specify the version of asynMotor we want to build against
motorBase_VERSION=7.2.2
LIBVERSION=homeerror
# These headers allow to depend on this library for derived drivers.
HEADERS += src/masterMacsAxis.h
HEADERS += src/masterMacsController.h

View File

@@ -11,11 +11,6 @@
#include <string.h>
#include <unistd.h>
enum moveMode {
positionMode,
velocityMode,
};
struct masterMacsAxisImpl {
/*
The axis status and axis error of MasterMACS are given as an integer from
@@ -29,9 +24,6 @@ struct masterMacsAxisImpl {
bool needInit = true;
bool targetReachedUninitialized;
bool dynamicLimits;
bool canPositionMove;
bool canVelocityMove;
moveMode lastMoveCommand;
};
/*
@@ -102,9 +94,6 @@ masterMacsAxis::masterMacsAxis(masterMacsController *pC, int axisNo)
.timeAtHandshake = 0,
.targetReachedUninitialized = true,
.dynamicLimits = false,
.canPositionMove = true,
.canVelocityMove = false,
.lastMoveCommand = positionMode,
})) {
asynStatus status = asynSuccess;
@@ -202,7 +191,6 @@ asynStatus masterMacsAxis::init() {
double motVmax = 0.0;
double motAccel = 0.0;
int dynamicLimits = 0;
int possibleModes = 0;
// =========================================================================
@@ -272,7 +260,7 @@ asynStatus masterMacsAxis::init() {
__PRETTY_FUNCTION__, __LINE__);
}
status = setVeloFields(abs(motVelocity), 0.0, motVmax);
status = setVeloFields(motVelocity, 0.0, motVmax);
if (status != asynSuccess) {
return status;
}
@@ -316,39 +304,6 @@ asynStatus masterMacsAxis::init() {
}
pMasterMacsA_->dynamicLimits = bool(dynamicLimits);
// Check if the motor can switch its mode
status = pC_->read(axisNo_, 31, response);
if (status != asynSuccess) {
return status;
}
nvals = sscanf(response, "%d", &possibleModes);
if (nvals != 1) {
return pC_->couldNotParseResponse("R31", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
switch (possibleModes) {
case 1:
pMasterMacsA_->canPositionMove = true;
pMasterMacsA_->canVelocityMove = false;
break;
case 2:
pMasterMacsA_->canPositionMove = false;
pMasterMacsA_->canVelocityMove = true;
break;
case 3:
pMasterMacsA_->canPositionMove = true;
pMasterMacsA_->canVelocityMove = true;
break;
default:
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line "
"%d:\nunexpected answer %d for R31 (possible operation "
"modes). Expected one of 1, 2 or 3.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
possibleModes);
}
status = readEncoderType();
if (status != asynSuccess) {
return status;
@@ -431,7 +386,10 @@ asynStatus masterMacsAxis::readLimits() {
highLimit = highLimit - limitsOffset;
lowLimit = lowLimit + limitsOffset;
return setLimits(highLimit, lowLimit);
setAxisParamChecked(this, motorHighLimitFromDriver, highLimit);
setAxisParamChecked(this, motorLowLimitFromDriver, lowLimit);
return status;
}
// Perform the actual poll
@@ -441,10 +399,10 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
asynStatus poll_status = asynSuccess;
// Status of read-write-operations of ASCII commands to the controller
asynStatus rwStatus = asynSuccess;
asynStatus rw_status = asynSuccess;
// Status of parameter library operations
asynStatus plStatus = asynSuccess;
asynStatus pl_status = asynSuccess;
char response[pC_->MAXBUF_] = {0};
int nvals = 0;
@@ -493,8 +451,8 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
}
pC_->read(axisNo_, 86, response);
if (rwStatus != asynSuccess) {
return rwStatus;
if (rw_status != asynSuccess) {
return rw_status;
}
nvals = sscanf(response, "%lf", &handshakePerformed);
@@ -520,20 +478,38 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
getAxisParamChecked(this, motorRecResolution, &motorRecResolution);
// Read the previous motor position
plStatus = motorPosition(&previousPosition);
if (plStatus != asynSuccess) {
return plStatus;
pl_status = motorPosition(&previousPosition);
if (pl_status != asynSuccess) {
return pl_status;
}
// Update the axis status
rwStatus = readAxisStatus();
if (rwStatus != asynSuccess) {
return rwStatus;
rw_status = readAxisStatus();
if (rw_status != asynSuccess) {
return rw_status;
}
rwStatus = pC_->read(axisNo_, 12, response);
if (rwStatus != asynSuccess) {
return rwStatus;
// If we wait for a handshake, but the motor was moving in its last poll
// cycle and has reached its target, it is not moving. Otherwise it is
// considered moving, even if we're still waiting for the handshake.
if (pMasterMacsA_->targetReachedUninitialized) {
*moving = false;
} else {
if (targetReached() || !switchedOn()) {
*moving = false;
} else {
*moving = true;
}
}
if (targetReached()) {
pMasterMacsA_->targetReachedUninitialized = false;
}
// Read the current position
rw_status = pC_->read(axisNo_, 12, response);
if (rw_status != asynSuccess) {
return rw_status;
}
nvals = sscanf(response, "%lf", &currentPosition);
if (nvals != 1) {
@@ -541,60 +517,12 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
__PRETTY_FUNCTION__, __LINE__);
}
plStatus = setMotorPosition(currentPosition);
if (plStatus != asynSuccess) {
return plStatus;
}
setAxisParamChecked(this, motorEncoderPosition, currentPosition);
if (pMasterMacsA_->lastMoveCommand == velocityMode && !speedEqualZero()) {
// TODO: Not sure whether the RVEL field of the motor record does not
// work - has to be clarified
double actualVelocity = 0.0;
rwStatus = pC_->read(axisNo_, 14, response);
if (rwStatus != asynSuccess) {
return rwStatus;
}
nvals = sscanf(response, "%lf", &actualVelocity);
if (nvals != 1) {
return pC_->couldNotParseResponse("R14", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
// Write the actual velocity to the paramLib (TODO: does it though?)
setAxisParamChecked(this, motorVelocity, actualVelocity);
// Motor is moving in velocity mode
*moving = true;
} else {
// If we wait for a handshake, but the motor was moving in its last poll
// cycle and has reached its target, it is not moving. Otherwise it is
// considered moving, even if we're still waiting for the handshake.
if (pMasterMacsA_->targetReachedUninitialized) {
*moving = false;
} else {
if (targetReached() || !switchedOn()) {
*moving = false;
} else {
*moving = true;
}
}
if (targetReached()) {
pMasterMacsA_->targetReachedUninitialized = false;
}
}
/*
Read out the error if either a fault condition status flag has been set
or if a movement has just ended.
Read out the error if either a fault condition status flag has been set or
if a movement has just ended.
*/
if (faultConditionSet() || !(*moving)) {
rwStatus = readAxisError();
rw_status = readAxisError();
}
msgPrintControlKey keyError = msgPrintControlKey(
@@ -668,42 +596,83 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
}
/*
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 the motor is homing or has been homed, ignore limit switch errors.
*/
int homing = 0;
int homed = 0;
getAxisParamChecked(this, motorStatusHome, &homing);
getAxisParamChecked(this, motorStatusHomed, &homed);
/*
Ignore limit switch errors when homing / motor has been homed or when
the motor is moving.
Background:
MasterMACS controllers move the motor outside the allowed range defined
by the "software limits" defined within the controllers because they
need to hit the physical end switch to determine the motor position. The
motor then rests close to the end switch, which might be outside the
controller-side software limits. This leads to this error, which is then
forwarded to the user even though nothing went wrong. The three checks
are here to prevent this:
- "homing": Is set at the start of a homing maneuver and removed once
the motor does not move anymore => Prevents the error from showing up
during the homing procedure
- "homed": Is set after a homing maneuver has been finished => Prevents
the error from showing up while the motor is resting idle outside the
software limits.
- "moving": Prevents the error from showing up when moving out of the
homing position.
If the motor hits the limits during normal operation, it is stopped by
the controller. Once stopped, moving is false and then the error is
shown to the user (because "homed" is not set).
Note: strictly speaking, it is not necessary to check homing because
moving would be set to true anyway. The check is here for clarity /
being explicit.
*/
if (positiveLimitSwitch() || negativeLimitSwitch() ||
positiveSoftwareLimit() || negativeSoftwareLimit()) {
if (!homing && !homed && !(*moving)) {
/*
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 the motor moved outside the limits while homing
(but in that case, the error is not shown, see previous
if-statement).
*/
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.");
}
// 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(
errorMessage, sizeof(errorMessage),
"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.");
// Generic error message for user
appendErrorMessage(
errorMessage, sizeof(errorMessage),
"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;
poll_status = asynError;
}
}
if (overCurrent()) {
@@ -774,9 +743,9 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
// Read out the limits, if the motor is not moving and if the limits are
// dynamic
if (pMasterMacsA_->dynamicLimits && !(*moving)) {
rwStatus = readLimits();
if (rwStatus != asynSuccess) {
return rwStatus;
rw_status = readLimits();
if (rw_status != asynSuccess) {
return rw_status;
}
}
@@ -801,11 +770,16 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
setAxisParamChecked(this, motorStatusDone, !(*moving));
setAxisParamChecked(this, motorStatusDirection, direction);
pl_status = setMotorPosition(currentPosition);
if (pl_status != asynSuccess) {
return pl_status;
}
return poll_status;
}
asynStatus masterMacsAxis::moveVelocity(double minVelocity, double maxVelocity,
double acceleration) {
asynStatus masterMacsAxis::doMoveVelocity(double minVelocity,
double maxVelocity,
double acceleration) {
// Suppress unused variable warning
(void)minVelocity;
(void)acceleration;
@@ -821,18 +795,6 @@ asynStatus masterMacsAxis::moveVelocity(double minVelocity, double maxVelocity,
// =========================================================================
// Can the motor be operated in velocity mode?
if (!pMasterMacsA_->canVelocityMove) {
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nAxis cannot "
"operate in velocity mode.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
setAxisParamChecked(this, motorStatusProblem, true);
setAxisParamChecked(this, motorMessageText,
"cannot operate in velocity mode");
return asynError;
}
getAxisParamChecked(this, motorEnableRBV, &enabled);
getAxisParamChecked(this, motorRecResolution, &motorRecResolution);
@@ -868,14 +830,7 @@ asynStatus masterMacsAxis::moveVelocity(double minVelocity, double maxVelocity,
// Start the move. We do not use the MovTimeout watchdog here, because the
// motor can move for any time in velocity mode.
status = pC_->write(axisNo_, 00, "3", timeout);
if (status != asynSuccess) {
return status;
}
// Cache the information that the current movement is in velocity mode
pMasterMacsA_->lastMoveCommand = velocityMode;
return status;
return pC_->write(axisNo_, 00, "3", timeout);
}
asynStatus masterMacsAxis::doMove(double position, int relative,
@@ -898,18 +853,6 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
// =========================================================================
// Can the motor be operated in position mode?
if (!pMasterMacsA_->canPositionMove) {
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nAxis cannot "
"operate in position mode.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
setAxisParamChecked(this, motorStatusProblem, true);
setAxisParamChecked(this, motorMessageText,
"cannot operate in position mode");
return asynError;
}
getAxisParamChecked(this, motorEnableRBV, &enabled);
getAxisParamChecked(this, motorRecResolution, &motorRecResolution);
@@ -995,8 +938,6 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
return asynError;
}
// Cache the information that the current movement is in position mode
pMasterMacsA_->lastMoveCommand = positionMode;
return status;
}
@@ -1297,8 +1238,8 @@ asynStatus masterMacsAxis::readAxisStatus() {
// =========================================================================
asynStatus rwStatus = pC_->read(axisNo_, 10, response);
if (rwStatus == asynSuccess) {
asynStatus rw_status = pC_->read(axisNo_, 10, response);
if (rw_status == asynSuccess) {
float axisStatus = 0;
int nvals = sscanf(response, "%f", &axisStatus);
@@ -1310,7 +1251,7 @@ asynStatus masterMacsAxis::readAxisStatus() {
pMasterMacsA_->axisStatus = toBitset(axisStatus);
}
return rwStatus;
return rw_status;
}
asynStatus masterMacsAxis::readAxisError() {
@@ -1318,8 +1259,8 @@ asynStatus masterMacsAxis::readAxisError() {
// =========================================================================
asynStatus rwStatus = pC_->read(axisNo_, 11, response);
if (rwStatus == asynSuccess) {
asynStatus rw_status = pC_->read(axisNo_, 11, response);
if (rw_status == asynSuccess) {
float axisError = 0;
int nvals = sscanf(response, "%f", &axisError);
@@ -1329,7 +1270,7 @@ asynStatus masterMacsAxis::readAxisError() {
}
pMasterMacsA_->axisError = toBitset(axisError);
}
return rwStatus;
return rw_status;
}
bool masterMacsAxis::readyToBeSwitchedOn() {
@@ -1356,8 +1297,6 @@ bool masterMacsAxis::remoteMode() { return pMasterMacsA_->axisStatus[9]; }
bool masterMacsAxis::targetReached() { return pMasterMacsA_->axisStatus[10]; }
bool masterMacsAxis::speedEqualZero() { return pMasterMacsA_->axisStatus[12]; }
bool masterMacsAxis::internalLimitActive() {
return pMasterMacsA_->axisStatus[11];
}

View File

@@ -52,15 +52,17 @@ class HIDDEN masterMacsAxis : public sinqAxis {
asynStatus doPoll(bool *moving);
/**
* @brief TODO
* @brief Implementation of the `doMoveVelocity` function from sinqAxis. The
* parameters are described in the documentation of
* `sinqAxis::doMoveVelocity`.
*
* @param minVelocity
* @param maxVelocity
* @param acceleration
* @return asynStatus
*/
asynStatus moveVelocity(double minVelocity, double maxVelocity,
double acceleration);
asynStatus doMoveVelocity(double minVelocity, double maxVelocity,
double acceleration);
/**
* @brief Implementation of the `doMove` function from sinqAxis. The
@@ -250,12 +252,6 @@ class HIDDEN masterMacsAxis : public sinqAxis {
*/
bool internalLimitActive();
/**
* @brief Read the property from axisStatus (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/
bool speedEqualZero();
// Bits 12 and 13 are unused
/**