13 Commits
0.1.0 ... 1.0.0

12 changed files with 136 additions and 86 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "turboPmac"]
path = turboPmac
url = https://gitea.psi.ch/lin-epics-modules/turboPmac

View File

@ -12,12 +12,6 @@ REQUIRED+=motorBase
# Specify the version of motorBase we want to build against
motorBase_VERSION=7.2.2
# Specify the version of sinqMotor we want to build against
sinqMotor_VERSION=0.11.0
# Specify the version of turboPmac we want to build against
turboPmac_VERSION=0.10.0
# These headers allow to depend on this library for derived drivers.
HEADERS += src/seleneGuideController.h
HEADERS += src/seleneLiftAxis.h
@ -25,15 +19,27 @@ HEADERS += src/seleneAngleAxis.h
HEADERS += src/seleneOffsetAxis.h
# Source files to build
SOURCES += turboPmac/sinqMotor/src/msgPrintControl.cpp
SOURCES += turboPmac/sinqMotor/src/sinqAxis.cpp
SOURCES += turboPmac/sinqMotor/src/sinqController.cpp
SOURCES += turboPmac/src/pmacAsynIPPort.c
SOURCES += turboPmac/src/turboPmacAxis.cpp
SOURCES += turboPmac/src/turboPmacController.cpp
SOURCES += turboPmac/src/pmacAsynIPPort.c
SOURCES += src/seleneGuideController.cpp
SOURCES += src/seleneLiftAxis.cpp
SOURCES += src/seleneAngleAxis.cpp
SOURCES += src/seleneOffsetAxis.cpp
# Store the record files
TEMPLATES += turboPmac/sinqMotor/db/asynRecord.db
TEMPLATES += turboPmac/sinqMotor/db/sinqMotor.db
TEMPLATES += turboPmac/db/turboPmac.db
TEMPLATES += db/seleneGuide.db
# This file registers the motor-specific functions in the IOC shell.
DBDS += turboPmac/sinqMotor/src/sinqMotor.dbd
DBDS += turboPmac/src/turboPmac.dbd
DBDS += src/seleneGuide.dbd
USR_CFLAGS += -Wall -Wextra -Weffc++ -Wunused-result -Wextra -Werror # -Wpedantic // Does not work because EPICS macros trigger warnings

View File

@ -90,13 +90,13 @@ seleneVirtualAxes("$(NAME)", 1, 2, 3, 4, 5, 6, 9, 10);
# Parametrize the EPICS record database with the substitution file named after the motor controller.
# In order to provide the PVs for absolute position and normalization, the
# corresponding `seleneGuide.db` file is loaded here as well.
epicsEnvSet("SINQDBPATH","$(sinqMotor_DB)/sinqMotor.db")
epicsEnvSet("SINQDBPATH","$(seleneGuide_DB)/sinqMotor.db")
dbLoadTemplate("$(TOP)/motors/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$(NAME)")
epicsEnvSet("SINQDBPATH","$(turboPmac_DB)/turboPmac.db")
epicsEnvSet("SINQDBPATH","$(seleneGuide_DB)/turboPmac.db")
dbLoadTemplate("$(TOP)/motors/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$(NAME)")
epicsEnvSet("SINQDBPATH","$(seleneGuide_DB)/seleneGuide.db")
dbLoadTemplate("$(TOP)/motors/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$(NAME)")
dbLoadRecords("$(sinqMotor_DB)/asynRecord.db","P=$(INSTR)$(NAME),PORT=$(ASYN_PORT)")
dbLoadRecords("$(seleneGuide_DB)/asynRecord.db","P=$(INSTR)$(NAME),PORT=$(ASYN_PORT)")
```
### Substitution file
@ -243,7 +243,7 @@ $z_{iH} = z_i + \mathrm{DHLM}_i$
$z_{iL} = z_i + \mathrm{DLLM}_i$
If $z_{iH} would exceed the corresponding absolute high limit $z_{iH,abs}$, it is set to $z_{iH,abs}$. The same holds true for the lower limits.
If $z_{iH}$ would exceed the corresponding absolute high limit $z_{iH,abs}$, it is set to $z_{iH,abs}$. The same holds true for the lower limits.
#### Lift axis
@ -277,6 +277,7 @@ For axes which are located behind the center of rotation (lever arm $x_i - x_{gu
$\alpha_{6H} = -\arctan((z_{6L} - z_{guide} - \Delta z_6) / (x_6 - x_{guide}))$
$\alpha_{6L} = -\arctan((z_{6H} - z_{guide} - \Delta z_6) / (x_6 - x_{guide}))$
### Versioning

View File

@ -22,8 +22,24 @@ seleneAngleAxis::seleneAngleAxis(seleneGuideController *pController, int axisNo,
pC_->paramLibAccessFailed(status, "motorCanDisable", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
// 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);
}
}
seleneAngleAxis::~seleneAngleAxis() {}
asynStatus seleneAngleAxis::stop(double acceleration) {
return liftAxis_->stop(acceleration);
}
@ -40,26 +56,25 @@ asynStatus seleneAngleAxis::doMove(double position, int relative,
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
targetPosition_ = position * motorRecResolution;
setTargetPosition(position * motorRecResolution);
targetSet_ = true;
return liftAxis_->startCombinedMoveFromVirtualAxis();
targetSet_ = false;
}
asynStatus seleneAngleAxis::targetPosition(double *targetPosition) {
asynStatus status = asynSuccess;
double seleneAngleAxis::targetPosition() {
if (targetSet_) {
*targetPosition = targetPosition_;
return turboPmacAxis::targetPosition();
} else {
status = motorPosition(targetPosition);
double targetPos = 0;
motorPosition(&targetPos);
return targetPos;
}
return status;
}
asynStatus seleneAngleAxis::doPoll(bool *moving) {
char userMessage[pC_->MAXBUF_] = {0};
char errorMessage[pC_->MAXBUF_] = {0};
// In the doPoll method of `seleneLiftAxis`, the parameters
// `motorStatusMoving` and `motorStatusDone` of this axis have already been
@ -189,15 +204,15 @@ asynStatus seleneAngleAxis::doPoll(bool *moving) {
// axis.
pl_status =
pC_->getStringParam(liftAxis_->axisNo(), pC_->motorMessageText(),
sizeof(userMessage), userMessage);
sizeof(errorMessage), errorMessage);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText",
liftAxis_->axisNo(),
__PRETTY_FUNCTION__, __LINE__);
}
if (strlen(userMessage) != 0) {
pl_status = setStringParam(pC_->motorMessageText(), userMessage);
if (strlen(errorMessage) != 0) {
pl_status = setStringParam(pC_->motorMessageText(), errorMessage);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,

View File

@ -32,6 +32,8 @@ class seleneAngleAxis : public turboPmacAxis {
seleneAngleAxis(seleneGuideController *pController, int axisNo,
seleneLiftAxis *liftAxis);
virtual ~seleneAngleAxis();
/**
* @brief Implementation of the `stop` function from asynMotorAxis
*
@ -100,13 +102,11 @@ class seleneAngleAxis : public turboPmacAxis {
double acceleration, int forwards);
/**
* @brief Read the target position from the axis and write it into the
* provided pointer.
* @brief Override of the superclass method
*
* @param targetPosition
* @return asynStatus
* @return double
*/
asynStatus targetPosition(double *targetPosition);
double targetPosition();
/**
* @brief Set the offsets of axis 3 and 4 to zero and recalculate all other

View File

@ -48,6 +48,8 @@ seleneGuideController::seleneGuideController(
}
}
seleneGuideController::~seleneGuideController() {}
asynStatus seleneGuideController::writeInt32(asynUser *pasynUser,
epicsInt32 value) {
int function = pasynUser->reason;

View File

@ -34,6 +34,8 @@ class seleneGuideController : public turboPmacController {
int numAxes, double movingPollPeriod,
double idlePollPeriod, double comTimeout);
virtual ~seleneGuideController();
/**
* @brief Overloaded function of turboPmacController
*

View File

@ -60,7 +60,7 @@ seleneLiftAxis::seleneLiftAxis(seleneGuideController *pC, int axis1No,
for (int i = 0; i < numAxes_; i++) {
oAxis = dynamic_cast<seleneOffsetAxis *>(pC->getAxis(axisNos[i]));
if (oAxis == nullptr) {
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_ERROR,
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nFATAL ERROR "
"(given axis number %d is not an instance of "
"seleneOffsetAxis).\nTerminating IOC.\n",
@ -99,8 +99,24 @@ seleneLiftAxis::seleneLiftAxis(seleneGuideController *pC, int axis1No,
pC_->paramLibAccessFailed(status, "motorCanDisable", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
// 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);
}
}
seleneLiftAxis::~seleneLiftAxis() {}
asynStatus seleneLiftAxis::init() {
// Local variable declaration
@ -118,7 +134,7 @@ asynStatus seleneLiftAxis::init() {
&motorRecResolution);
if (status == asynParamUndefined) {
if (now + maxInitTime < time(NULL)) {
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_ERROR,
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line "
"%d\nInitializing the parameter library failed.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__,
@ -191,7 +207,7 @@ asynStatus seleneLiftAxis::normalizePositions() {
double lift = 0.0;
double angle = 0.0;
asynStatus status = asynSuccess;
char response[pC_->MAXBUF_];
char response[pC_->MAXBUF_] = {0};
double encoderPositions[6] = {0.0};
double absolutePositions[6] = {0.0};
int nvals = 0;
@ -268,7 +284,8 @@ asynStatus seleneLiftAxis::doPoll(bool *moving) {
// Status of parameter library operations
asynStatus pl_status = asynSuccess;
char response[pC_->MAXBUF_], userMessage[pC_->MAXBUF_] = {0};
char response[pC_->MAXBUF_] = {0};
char userMessage[pC_->MAXBUF_] = {0};
int nvals = 0;
int axStatus = 0;
@ -455,57 +472,41 @@ asynStatus seleneLiftAxis::doMove(double position, int relative,
return pC_->paramLibAccessFailed(status, "motorRecResolution_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
targetPosition_ = position * motorRecResolution;
setTargetPosition(position * motorRecResolution);
targetSet_ = true;
status = startCombinedMoveFromVirtualAxis();
targetSet_ = false;
return status;
}
asynStatus seleneLiftAxis::targetPosition(double *targetPosition) {
asynStatus status = asynSuccess;
double seleneLiftAxis::targetPosition() {
if (targetSet_) {
*targetPosition = targetPosition_;
return turboPmacAxis::targetPosition();
} else {
status = motorPosition(targetPosition);
double targetPos = 0;
motorPosition(&targetPos);
return targetPos;
}
return status;
}
asynStatus seleneLiftAxis::startCombinedMove() {
char command[pC_->MAXBUF_], response[pC_->MAXBUF_];
char command[pC_->MAXBUF_] = {0};
char response[pC_->MAXBUF_] = {0};
double liftTargetPosition = 0.0;
double angleTargetPosition = 0.0;
asynStatus status = asynSuccess;
// =========================================================================
status = targetPosition(&liftTargetPosition);
if (status != asynSuccess) {
return status;
}
status = angleAxis_->targetPosition(&angleTargetPosition);
if (status != asynSuccess) {
return status;
}
errlogPrintf("Target lift: %lf, Target angle: %lf\n", liftTargetPosition,
angleTargetPosition);
liftTargetPosition = targetPosition();
angleTargetPosition = angleAxis_->targetPosition();
auto targetPositions =
positionsFromLiftAndAngle(liftTargetPosition, angleTargetPosition);
// Apply the offsets of the individual offset axes
for (int i = 0; i < numAxes_; i++) {
double offsetTargetPosition = 0.0;
status = offsetAxes_[i]->targetPosition(&offsetTargetPosition);
if (status != asynSuccess) {
return status;
}
targetPositions[i] = targetPositions[i] + offsetTargetPosition;
targetPositions[i] =
targetPositions[i] + offsetAxes_[i]->targetPosition();
}
// Set all target positions and send the move command
@ -514,8 +515,6 @@ asynStatus seleneLiftAxis::startCombinedMove() {
targetPositions[0], targetPositions[1], targetPositions[2],
targetPositions[3], targetPositions[4], targetPositions[5]);
errlogPrintf("%s\n", command);
// No answer expected
return pC_->writeRead(axisNo_, command, response, 0);
}
@ -527,7 +526,7 @@ asynStatus seleneLiftAxis::stop(double acceleration) {
// Status of parameter library operations
asynStatus pl_status = asynSuccess;
char response[pC_->MAXBUF_];
char response[pC_->MAXBUF_] = {0};
// =========================================================================
@ -536,7 +535,7 @@ asynStatus seleneLiftAxis::stop(double acceleration) {
if (rw_status != asynSuccess) {
asynPrint(
pC_->asynUserSelf(), ASYN_TRACE_ERROR,
pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nStopping the movement "
"failed.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
@ -586,7 +585,7 @@ asynStatus seleneLiftAxis::readEncoderType() {
asynStatus seleneLiftAxis::doHome(double minVelocity, double maxVelocity,
double acceleration, int forwards) {
char response[pC_->MAXBUF_];
char response[pC_->MAXBUF_] = {0};
// No answer expected
return pC_->writeRead(axisNo_,

View File

@ -39,6 +39,8 @@ class seleneLiftAxis : public turboPmacAxis {
int axis3No, int axis4No, int axis5No, int axis6No,
int liftAxisNo, int angleAxisNo);
virtual ~seleneLiftAxis();
/**
* @brief Implementation of the `stop` function from asynMotorAxis
*
@ -254,13 +256,11 @@ class seleneLiftAxis : public turboPmacAxis {
bool virtualAxisMovement() { return virtualAxisMovement_; }
/**
* @brief Read the target position from the axis and write it into the
* provided pointer.
* @brief Override of the superclass method
*
* @param targetPosition
* @return asynStatus
* @return double
*/
asynStatus targetPosition(double *targetPosition);
double targetPosition();
/**
* @brief Access the position of the lift axis

View File

@ -80,8 +80,24 @@ seleneOffsetAxis::seleneOffsetAxis(seleneGuideController *pController,
pC_->paramLibAccessFailed(status, "motorCanDisable", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
// 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);
}
}
seleneOffsetAxis::~seleneOffsetAxis() {}
asynStatus seleneOffsetAxis::init() {
// Local variable declaration
@ -99,7 +115,7 @@ asynStatus seleneOffsetAxis::init() {
&motorRecResolution);
if (status == asynParamUndefined) {
if (now + maxInitTime < time(NULL)) {
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_ERROR,
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line "
"%d\nInitializing the parameter library failed.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__,
@ -135,15 +151,14 @@ asynStatus seleneOffsetAxis::init() {
return status;
}
asynStatus seleneOffsetAxis::targetPosition(double *targetPosition) {
asynStatus status = asynSuccess;
double seleneOffsetAxis::targetPosition() {
if (targetSet_) {
*targetPosition = targetPosition_;
return turboPmacAxis::targetPosition();
} else {
status = motorPosition(targetPosition);
double targetPos = 0;
motorPosition(&targetPos);
return targetPos;
}
return status;
}
asynStatus seleneOffsetAxis::doPoll(bool *moving) {
@ -156,8 +171,9 @@ asynStatus seleneOffsetAxis::doPoll(bool *moving) {
int error = 0;
int nvals = 0;
char command[pC_->MAXBUF_], response[pC_->MAXBUF_],
userMessage[pC_->MAXBUF_];
char command[pC_->MAXBUF_] = {0};
char response[pC_->MAXBUF_] = {0};
char userMessage[pC_->MAXBUF_] = {0};
// =========================================================================
@ -198,6 +214,12 @@ asynStatus seleneOffsetAxis::doPoll(bool *moving) {
// Update the parameter library
if (error != 0) {
status = setStringParam(pC_->motorMessageText(), userMessage);
if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "motorMessageText",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
status = setIntegerParam(pC_->motorStatusProblem(), true);
if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "motorStatusProblem_",
@ -207,8 +229,8 @@ asynStatus seleneOffsetAxis::doPoll(bool *moving) {
}
// TODO: To be removed, once the real limits are derived
//highLimit = 10.0;
//lowLimit = -10.0;
// highLimit = 10.0;
// lowLimit = -10.0;
// Convert into lift coordinate system
absoluteHighLimitLiftCS_ = highLimit - zOffset_;
@ -309,7 +331,7 @@ asynStatus seleneOffsetAxis::doMove(double position, int relative,
}
// Set the target position
targetPosition_ = position * motorRecResolution;
setTargetPosition(position * motorRecResolution);
targetSet_ = true;
asynStatus status = liftAxis_->startCombinedMoveFromOffsetAxis();
targetSet_ = false;
@ -368,7 +390,6 @@ asynStatus seleneOffsetAxisCreateAxis(const char *portName, int axisNo,
portName, __PRETTY_FUNCTION__, __LINE__);
return asynError;
}
// Unsafe cast of the pointer to an asynPortDriver
asynPortDriver *apd = (asynPortDriver *)(ptr);
// Safe downcast

View File

@ -30,6 +30,8 @@ class seleneOffsetAxis : public turboPmacAxis {
seleneOffsetAxis(seleneGuideController *pController, int axisNo,
double xPos, double zPos);
virtual ~seleneOffsetAxis();
/**
* @brief Initialize this offset axis
*
@ -79,13 +81,11 @@ class seleneOffsetAxis : public turboPmacAxis {
asynStatus setPositionsFromEncoderPosition(double encoderPosition);
/**
* @brief Read the target position from the axis and write it into the
* provided pointer.
* @brief Override of the superclass method
*
* @param targetPosition
* @return asynStatus
* @return double
*/
asynStatus targetPosition(double *targetPosition);
double targetPosition();
/**
* @brief Set the offsets of axis 3 and 4 to zero and recalculate all other

1
turboPmac Submodule

Submodule turboPmac added at 4bc3388bc6