28 Commits

Author SHA1 Message Date
f5287609e5 Updated to turboPmac 1.6.3
Some checks failed
Test And Build / Lint (push) Successful in 3s
Test And Build / Build (push) Failing after 7s
2026-02-11 11:16:10 +01:00
e322dd09c1 Updated turboPmac version to 1.6.2 2026-02-03 14:08:28 +01:00
754fc16cd3 Updated to turboPmac 1.6.1
Some checks failed
Test And Build / Lint (push) Successful in 4s
Test And Build / Build (push) Failing after 7s
2026-02-03 13:47:10 +01:00
a7cb1cd05f Readded correct version of turboPmac
Some checks failed
Test And Build / Lint (push) Successful in 3s
Test And Build / Build (push) Failing after 7s
2026-01-23 13:28:38 +01:00
f9d5d1aaee Completely removed turboPmac temporarily 2026-01-23 13:27:47 +01:00
ef81f38017 Added correct repo for turboPMAC 2026-01-23 11:47:48 +01:00
6ee554f9e5 Remove turboPmac submodule 2026-01-23 11:46:45 +01:00
7bd3adbfc3 Remove turboPmac submodule 2026-01-23 11:46:12 +01:00
bff7ad79df Fully remove turboPmac submodule 2026-01-23 11:44:46 +01:00
70029b12b2 Temporarily removed turboPmac module 2026-01-23 11:42:59 +01:00
ac5ab3855e Updated turboPmac version and added constructor for standard axis 2026-01-23 11:38:18 +01:00
2c9de618ec Made code compile with compiler flags
Some checks failed
Test And Build / Lint (push) Successful in 4s
Test And Build / Build (push) Failing after 7s
2025-12-23 14:00:08 +01:00
b834dc3f8d Updated turboPmac version 2025-12-23 13:51:18 +01:00
ee6235e74a Removed C++ only flag from USR_CFLAGS and updated turboPmac version
Some checks failed
Test And Build / Lint (push) Successful in 3s
Test And Build / Build (push) Failing after 9s
2025-08-22 15:30:33 +02:00
48c068826c Updated sinqMotor to fix bug in forcedPoll regarding watchdog
Some checks failed
Test And Build / Lint (push) Successful in 4s
Test And Build / Build (push) Failing after 10s
2025-08-14 17:25:35 +02:00
c494fc88a5 Updated to turboPmac 1.4 and hid symbols
Some checks failed
Test And Build / Lint (push) Successful in 3s
Test And Build / Build (push) Failing after 9s
2025-08-12 09:28:06 +02:00
e0fa9ef3a9 also check header files
Some checks failed
Test And Build / Lint (push) Successful in 3s
Test And Build / Build (push) Failing after 10s
2025-07-04 14:37:43 +02:00
dc6d5c40b3 need to build sinqmotor first
Some checks failed
Test And Build / Lint (push) Successful in 4s
Test And Build / Build (push) Failing after 10s
2025-07-04 14:23:21 +02:00
56288b1db8 adds gitea action
Some checks failed
Test And Build / Lint (push) Successful in 3s
Test And Build / Build (push) Failing after 3s
2025-07-04 14:21:39 +02:00
dde15eb30d Updated param lib accessors to use macro to reduce boilerplate 2025-06-18 10:22:49 +02:00
efccb26302 Updated param lib accessors to use macro to reduce boilerplate 2025-06-18 10:19:43 +02:00
85d443a92b Updated turboPmac version 2025-06-18 08:56:32 +02:00
588ca158c1 Adjusted targetPosition function 2025-06-10 16:42:35 +02:00
3780dee24f Adjusted function for reading the target position 2025-06-10 16:37:02 +02:00
c736c191d0 Updated dependency turboPmac to 1.1.1 2025-06-10 16:30:01 +02:00
42784c2bff Updated dependencies 2025-05-16 16:24:42 +02:00
2636296539 Removed debug messages 2025-05-15 13:50:26 +02:00
b09f081db9 Updated turboPmac and sinqMotor versions 2025-05-15 13:35:16 +02:00
14 changed files with 437 additions and 475 deletions

View File

@@ -0,0 +1,30 @@
name: Test And Build
on: [push]
jobs:
Lint:
runs-on: linepics
steps:
- name: checkout repo
uses: actions/checkout@v4
- name: cppcheck
run: cppcheck --std=c++17 --addon=cert --addon=misc --error-exitcode=1 src/*.cpp
- name: formatting
run: clang-format --style=file --Werror --dry-run src/*.cpp src/*.h
Build:
runs-on: linepics
steps:
- name: checkout repo
uses: actions/checkout@v4
with:
submodules: 'true'
- run: |
pushd turboPmac
sed -i 's/ARCH_FILTER=.*/ARCH_FILTER=linux%/' Makefile
echo -e "\nIGNORE_SUBMODULES += sinqmotor" >> Makefile
make install
popd
- run: |
sed -i 's/ARCH_FILTER=.*/ARCH_FILTER=linux%/' Makefile
echo -e "\nIGNORE_SUBMODULES += turboPmac" >> Makefile
make install

View File

@@ -1,58 +0,0 @@
default:
image: docker.psi.ch:5000/sinqdev/sinqepics:latest
stages:
- lint
- build
- test
cppcheck:
stage: lint
script:
- cppcheck --std=c++17 --addon=cert --addon=misc --error-exitcode=1 src/*.cpp
artifacts:
expire_in: 1 week
tags:
- sinq
formatting:
stage: lint
script:
- clang-format --style=file --Werror --dry-run src/*.cpp
artifacts:
expire_in: 1 week
tags:
- sinq
# clangtidy:
# stage: lint
# script:
# - curl https://docker.psi.ch:5000/v2/_catalog
# # - dnf update -y
# # - dnf install -y clang-tools-extra
# # - clang-tidy sinqEPICSApp/src/*.cpp sinqEPICSApp/src/*.c sinqEPICSApp/src/*.h -checks=cppcoreguidelines-*,cert-*
# # tags:
# # - sinq
build_module:
stage: build
script:
- export SINQMOTOR_VERSION="$(grep 'sinqMotor_VERSION=' Makefile | cut -d= -f2)"
- git clone --depth 1 --branch "${SINQMOTOR_VERSION}" https://gitlab-ci-token:${CI_JOB_TOKEN}@git.psi.ch/sinq-epics-modules/sinqmotor.git
- pushd sinqmotor
- sed -i 's/ARCH_FILTER=.*/ARCH_FILTER=linux%/' Makefile
- echo "LIBVERSION=${SINQMOTOR_VERSION}" >> Makefile
- make install
- popd
- sed -i 's/ARCH_FILTER=.*/ARCH_FILTER=linux%/' Makefile
- echo "LIBVERSION=${CI_COMMIT_TAG:-0.0.1}" >> Makefile
- make install
- cp -rT "/ioc/modules/seleneLift/$(ls -U /ioc/modules/seleneLift/ | head -1)" "./seleneLift-${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}"
artifacts:
name: "seleneLift-${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}"
paths:
- "seleneLift-${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}/*"
expire_in: 1 week
when: always
tags:
- sinq

View File

@@ -42,4 +42,5 @@ DBDS += turboPmac/sinqMotor/src/sinqMotor.dbd
DBDS += turboPmac/src/turboPmac.dbd DBDS += turboPmac/src/turboPmac.dbd
DBDS += src/seleneGuide.dbd DBDS += src/seleneGuide.dbd
USR_CFLAGS += -Wall -Wextra -Weffc++ -Wunused-result -Wextra -Werror # -Wpedantic // Does not work because EPICS macros trigger warnings USR_CFLAGS += -Wall -Wextra -Wunused-result -Wextra -Werror # -Wpedantic // Does not work because EPICS macros trigger warnings
USR_CXXFLAGS += -Wall -Wextra -Wunused-result -Wextra -Werror

View File

@@ -44,7 +44,7 @@ Two additional PVs are provided in `db/seleneGuide.db`:
### Usage in IOC shell ### Usage in IOC shell
seleneGuide exports the following IOC shell functions: seleneGuide exports the following IOC shell functions:
- `seleneGuideController`: Create a new controller object. This object is essentially a `turboPmacController` object from the standard [Turbo PMAC driver](https://git.psi.ch/sinq-epics-modules/turboPmac), but it supports the additional motor PVs for absolute position and normalization. This means that it can be used with "normal" `turboPmacAxis` as well. - `seleneGuideController`: Create a new controller object. This object is essentially a `turboPmacController` object from the standard [Turbo PMAC driver](https://git.psi.ch/sinq-epics-modules/turboPmac), but it supports the additional motor PVs for absolute position and normalization. This means that it can be used to control a "standard" TurboPMAC axis as well. To ensure compatibility between controller and axis, it is recommended to use the wrapper `seleneStandardAxis` instead of the `turboPmacAxis` provided by the basic TurboPMAC driver (because otherwise the versions of the `seleneGuide` and the `turboPmac` drivers are not independent from each other).
- `seleneOffsetAxis`: Create a new offset axis object with the specified x- and z-offset from an arbitrary origin in mm. - `seleneOffsetAxis`: Create a new offset axis object with the specified x- and z-offset from an arbitrary origin in mm.
- `seleneVirtualAxes`: Create the virtual lift and the angle axes. - `seleneVirtualAxes`: Create the virtual lift and the angle axes.
@@ -78,8 +78,9 @@ seleneOffsetAxis("$(NAME)",5, 5933.99477, 0.0);
seleneOffsetAxis("$(NAME)",6, 7463.87259, 0.0); seleneOffsetAxis("$(NAME)",6, 7463.87259, 0.0);
# These are two "normal" PMAC axes which work the same way they would with a turboPmacController. # These are two "normal" PMAC axes which work the same way they would with a turboPmacController.
turboPmacAxis("$(NAME)",7); # Using the wrapper seleneStandardAxis ensures compatibility to the seleneGuideController.
turboPmacAxis("$(NAME)",8); seleneStandardAxis("$(NAME)",7);
seleneStandardAxis("$(NAME)",8);
# This function call creates the lift and the angle axes. # This function call creates the lift and the angle axes.
# The arguments on position 2 to 7 are the axis indices of the offset axes # The arguments on position 2 to 7 are the axis indices of the offset axes

View File

@@ -19,66 +19,79 @@ seleneAngleAxis::seleneAngleAxis(seleneGuideController *pController, int axisNo,
// Selene guide motors cannot be disabled // Selene guide motors cannot be disabled
status = pC_->setIntegerParam(axisNo_, pC_->motorCanDisable(), 0); status = pC_->setIntegerParam(axisNo_, pC_->motorCanDisable(), 0);
if (status != asynSuccess) { if (status != asynSuccess) {
pC_->paramLibAccessFailed(status, "motorCanDisable", axisNo_, asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
__PRETTY_FUNCTION__, __LINE__); "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);
}
// 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) { asynStatus seleneAngleAxis::stop(double acceleration) {
return liftAxis_->stop(acceleration); return liftAxis_->stop(acceleration);
} }
asynStatus seleneAngleAxis::doMove(double position, int relative, asynStatus seleneAngleAxis::doMove(double position, int relative,
double min_velocity, double max_velocity, double minVelocity, double maxVelocity,
double acceleration) { double acceleration) {
// Suppress unused variable warnings
(void)relative;
(void)minVelocity;
(void)maxVelocity;
(void)acceleration;
double motorRecResolution = 0.0; double motorRecResolution = 0.0;
asynStatus pl_status = pC_->getDoubleParam( getAxisParamChecked(this, motorRecResolution, &motorRecResolution);
axisNo_, pC_->motorRecResolution(), &motorRecResolution); setTargetPosition(position * motorRecResolution);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorRecResolution_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
targetPosition_ = position * motorRecResolution;
targetSet_ = true; targetSet_ = true;
return liftAxis_->startCombinedMoveFromVirtualAxis(); return liftAxis_->startCombinedMoveFromVirtualAxis();
targetSet_ = false; targetSet_ = false;
} }
asynStatus seleneAngleAxis::targetPosition(double *targetPosition) { double seleneAngleAxis::targetPosition() {
asynStatus status = asynSuccess;
if (targetSet_) { if (targetSet_) {
*targetPosition = targetPosition_; return turboPmacAxis::targetPosition();
} else { } else {
status = motorPosition(targetPosition); double targetPos = 0;
motorPosition(&targetPos);
return targetPos;
} }
return status;
} }
asynStatus seleneAngleAxis::doPoll(bool *moving) { asynStatus seleneAngleAxis::doPoll(bool *moving) {
char userMessage[pC_->MAXBUF_] = {0}; char errorMessage[pC_->MAXBUF_] = {0};
asynStatus status = asynSuccess;
// In the doPoll method of `seleneLiftAxis`, the parameters // In the doPoll method of `seleneLiftAxis`, the parameters
// `motorStatusMoving` and `motorStatusDone` of this axis have already been // `motorStatusMoving` and `motorStatusDone` of this axis have already been
// set accordingly. // set accordingly.
int isMoving = 0; getAxisParamChecked(this, motorStatusMoving, moving);
asynStatus pl_status =
pC_->getIntegerParam(axisNo(), pC_->motorStatusMoving(), &isMoving);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusMoving",
axisNo(), __PRETTY_FUNCTION__,
__LINE__);
}
*moving = (isMoving != 0);
double motPos = 0.0; double motPos = 0.0;
pl_status = motorPosition(&motPos); status = motorPosition(&motPos);
if (pl_status != asynSuccess) { if (status != asynSuccess) {
return pl_status; return status;
} }
/* /*
@@ -111,9 +124,9 @@ asynStatus seleneAngleAxis::doPoll(bool *moving) {
double smallestHighLimit = std::numeric_limits<double>::infinity(); double smallestHighLimit = std::numeric_limits<double>::infinity();
double smallestLowLimit = std::numeric_limits<double>::infinity(); double smallestLowLimit = std::numeric_limits<double>::infinity();
double liftPosition = 0.0; double liftPosition = 0.0;
pl_status = liftAxis_->motorPosition(&liftPosition); status = liftAxis_->motorPosition(&liftPosition);
if (pl_status != asynSuccess) { if (status != asynSuccess) {
return pl_status; return status;
} }
for (int i = 0; i < liftAxis_->numAxes_; i++) { for (int i = 0; i < liftAxis_->numAxes_; i++) {
@@ -122,9 +135,9 @@ asynStatus seleneAngleAxis::doPoll(bool *moving) {
double leverArm = oAxis->xOffset() - liftAxis_->xPos(); double leverArm = oAxis->xOffset() - liftAxis_->xPos();
double motPos = 0.0; double motPos = 0.0;
pl_status = oAxis->motorPosition(&motPos); status = oAxis->motorPosition(&motPos);
if (pl_status != asynSuccess) { if (status != asynSuccess) {
return pl_status; return status;
} }
/* /*
@@ -161,52 +174,23 @@ asynStatus seleneAngleAxis::doPoll(bool *moving) {
} }
} }
pl_status = pC_->setDoubleParam(axisNo_, pC_->motorHighLimitFromDriver(), setAxisParamChecked(this, motorHighLimitFromDriver, smallestHighLimit);
smallestHighLimit); setAxisParamChecked(this, motorLowLimitFromDriver, smallestLowLimit);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorHighLimitFromDriver",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
pl_status = pC_->setDoubleParam(axisNo_, pC_->motorLowLimitFromDriver(),
smallestLowLimit);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorLowLimitFromDriver",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
// This axis should always be seen as enabled, since it is automatically // This axis should always be seen as enabled, since it is automatically
// enabled before each movement and disabled afterwards // enabled before each movement and disabled afterwards
pl_status = setIntegerParam(pC_->motorEnableRBV(), 1); setAxisParamChecked(this, motorEnableRBV, true);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorEnableRBV_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
// If an error message is set in the liftAxis_, copy this message into this // If an error message is set in the liftAxis_, copy this message into this
// axis. // axis.
pl_status = getAxisParamChecked(liftAxis_, motorMessageText, &errorMessage);
pC_->getStringParam(liftAxis_->axisNo(), pC_->motorMessageText(),
sizeof(userMessage), userMessage);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText",
liftAxis_->axisNo(),
__PRETTY_FUNCTION__, __LINE__);
}
if (strlen(userMessage) != 0) { if (strlen(errorMessage) != 0) {
pl_status = setStringParam(pC_->motorMessageText(), userMessage); setAxisParamChecked(this, motorMessageText, errorMessage);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
return asynError; return asynError;
} }
return pl_status; return status;
} }
asynStatus seleneAngleAxis::doReset() { return liftAxis_->reset(); } asynStatus seleneAngleAxis::doReset() { return liftAxis_->reset(); }
@@ -214,13 +198,7 @@ asynStatus seleneAngleAxis::doReset() { return liftAxis_->reset(); }
asynStatus seleneAngleAxis::enable(bool on) { return liftAxis_->enable(on); } asynStatus seleneAngleAxis::enable(bool on) { return liftAxis_->enable(on); }
asynStatus seleneAngleAxis::readEncoderType() { asynStatus seleneAngleAxis::readEncoderType() {
asynStatus pl_status = setAxisParamChecked(this, encoderType, IncrementalEncoder);
setStringParam(pC_->encoderType(), IncrementalEncoder);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "encoderType_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
return asynSuccess; return asynSuccess;
} }

View File

@@ -1,22 +1,19 @@
#ifndef seleneAngleAxis_H #ifndef seleneAngleAxis_H
#define seleneAngleAxis_H #define seleneAngleAxis_H
#include "seleneGuideController.h"
#include "turboPmacAxis.h" #include "turboPmacAxis.h"
// Forward declaration of the seleneLiftAxis class to resolve the cyclic // Forward declaration of the seleneLiftAxis class to resolve the cyclic
// dependency between the seleneLiftAxis and the seleneAngleAxis .h-file. // dependency between the seleneLiftAxis and the seleneAngleAxis .h-file.
// See https://en.cppreference.com/w/cpp/language/class. // See https://en.cppreference.com/w/cpp/language/class.
class seleneLiftAxis; class HIDDEN seleneLiftAxis;
// Forward declaration of the seleneGuideController class to resolve the cyclic
// dependency. See https://en.cppreference.com/w/cpp/language/class.
class seleneGuideController;
/** /**
* @brief Virtual axis for setting the angle of the Selene guide * @brief Virtual axis for setting the angle of the Selene guide
* *
* Please see README.md for a detailed explanation. * Please see README.md for a detailed explanation.
*/ */
class seleneAngleAxis : public turboPmacAxis { class HIDDEN seleneAngleAxis : public turboPmacAxis {
public: public:
/** /**
* @brief Construct a new seleneAngleAxis. * @brief Construct a new seleneAngleAxis.
@@ -32,6 +29,8 @@ class seleneAngleAxis : public turboPmacAxis {
seleneAngleAxis(seleneGuideController *pController, int axisNo, seleneAngleAxis(seleneGuideController *pController, int axisNo,
seleneLiftAxis *liftAxis); seleneLiftAxis *liftAxis);
virtual ~seleneAngleAxis();
/** /**
* @brief Implementation of the `stop` function from asynMotorAxis * @brief Implementation of the `stop` function from asynMotorAxis
* *
@@ -49,12 +48,12 @@ class seleneAngleAxis : public turboPmacAxis {
* *
* @param position * @param position
* @param relative * @param relative
* @param min_velocity * @param minVelocity
* @param maxOffset_velocity * @param maxOffset_velocity
* @param acceleration * @param acceleration
* @return asynStatus * @return asynStatus
*/ */
asynStatus doMove(double position, int relative, double min_velocity, asynStatus doMove(double position, int relative, double minVelocity,
double maxOffset_velocity, double acceleration); double maxOffset_velocity, double acceleration);
/** /**
@@ -100,13 +99,11 @@ class seleneAngleAxis : public turboPmacAxis {
double acceleration, int forwards); double acceleration, int forwards);
/** /**
* @brief Read the target position from the axis and write it into the * @brief Override of the superclass method
* provided pointer.
* *
* @param targetPosition * @return double
* @return asynStatus
*/ */
asynStatus targetPosition(double *targetPosition); double targetPosition();
/** /**
* @brief Set the offsets of axis 3 and 4 to zero and recalculate all other * @brief Set the offsets of axis 3 and 4 to zero and recalculate all other
@@ -140,6 +137,11 @@ class seleneAngleAxis : public turboPmacAxis {
*/ */
asynStatus rereadEncoder() { return asynSuccess; } asynStatus rereadEncoder() { return asynSuccess; }
/**
* @brief Return a pointer to the axis controller
*/
virtual seleneGuideController *pController() override { return pC_; };
protected: protected:
seleneGuideController *pC_; seleneGuideController *pC_;
seleneLiftAxis *liftAxis_; seleneLiftAxis *liftAxis_;

View File

@@ -1,3 +1,4 @@
registrar(seleneVirtualAxesRegister) registrar(seleneVirtualAxesRegister)
registrar(seleneOffsetAxisRegister) registrar(seleneOffsetAxisRegister)
registrar(seleneStandardAxisRegister)
registrar(seleneGuideControllerRegister) registrar(seleneGuideControllerRegister)

View File

@@ -1,4 +1,7 @@
#include "seleneGuideController.h" #include "seleneGuideController.h"
#include "seleneAngleAxis.h"
#include "seleneLiftAxis.h"
#include "seleneOffsetAxis.h"
#include "turboPmacController.h" #include "turboPmacController.h"
#include <epicsExport.h> #include <epicsExport.h>
#include <errlog.h> #include <errlog.h>
@@ -20,7 +23,8 @@ seleneGuideController::seleneGuideController(
const char *portName, const char *ipPortConfigName, int numAxes, const char *portName, const char *ipPortConfigName, int numAxes,
double movingPollPeriod, double idlePollPeriod, double comTimeout) double movingPollPeriod, double idlePollPeriod, double comTimeout)
: turboPmacController(portName, ipPortConfigName, numAxes, movingPollPeriod, : turboPmacController(portName, ipPortConfigName, numAxes, movingPollPeriod,
idlePollPeriod, NUM_seleneGuide_DRIVER_PARAMS) idlePollPeriod, comTimeout,
NUM_seleneGuide_DRIVER_PARAMS)
{ {
@@ -48,6 +52,8 @@ seleneGuideController::seleneGuideController(
} }
} }
seleneGuideController::~seleneGuideController() {}
asynStatus seleneGuideController::writeInt32(asynUser *pasynUser, asynStatus seleneGuideController::writeInt32(asynUser *pasynUser,
epicsInt32 value) { epicsInt32 value) {
int function = pasynUser->reason; int function = pasynUser->reason;
@@ -140,7 +146,8 @@ static const iocshArg *const CreateControllerArgs[] = {
&CreateControllerArg0, &CreateControllerArg1, &CreateControllerArg2, &CreateControllerArg0, &CreateControllerArg1, &CreateControllerArg2,
&CreateControllerArg3, &CreateControllerArg4, &CreateControllerArg5}; &CreateControllerArg3, &CreateControllerArg4, &CreateControllerArg5};
static const iocshFuncDef configSeleneGuideCreateController = { static const iocshFuncDef configSeleneGuideCreateController = {
"seleneGuideController", 6, CreateControllerArgs}; "seleneGuideController", 6, CreateControllerArgs,
"Create a new instance of a Selene controller."};
static void configSeleneGuideCreateControllerCallFunc(const iocshArgBuf *args) { static void configSeleneGuideCreateControllerCallFunc(const iocshArgBuf *args) {
seleneGuideCreateController(args[0].sval, args[1].sval, args[2].ival, seleneGuideCreateController(args[0].sval, args[1].sval, args[2].ival,
args[3].dval, args[4].dval, args[5].dval); args[3].dval, args[4].dval, args[5].dval);
@@ -154,4 +161,97 @@ static void seleneGuideControllerRegister(void) {
} }
epicsExportRegistrar(seleneGuideControllerRegister); epicsExportRegistrar(seleneGuideControllerRegister);
/*
C wrapper for a "standard" TurboPMAC axis constructor. This function is
identical to "turboPmacCreateAxis" in the standard TurboPMAC driver. However, it
is recommended to use this constructor to make sure the version of the axis
matches that of the controller. The controller is read from the portName.
*/
asynStatus seleneStandardCreateAxis(const char *portName, int axis) {
/*
findAsynPortDriver is a asyn library FFI function which uses the C ABI.
Therefore it returns a void pointer instead of e.g. a pointer to a
superclass of the controller such as asynPortDriver. Type-safe upcasting
via dynamic_cast is therefore not possible directly. However, we do know
that the void pointer is either a pointer to asynPortDriver (if a driver
with the specified name exists) or a nullptr. Therefore, we first do a
nullptr check, then a cast to asynPortDriver and lastly a (typesafe)
dynamic_upcast to Controller
https://stackoverflow.com/questions/70906749/is-there-a-safe-way-to-cast-void-to-class-pointer-in-c
*/
void *ptr = findAsynPortDriver(portName);
if (ptr == nullptr) {
/*
We can't use asynPrint here since this macro would require us
to get an asynUser from a pointer to an asynPortDriver.
However, the given pointer is a nullptr and therefore doesn't
have an asynUser! printf is an EPICS alternative which
works w/o that, but doesn't offer the comfort provided
by the asynTrace-facility
*/
errlogPrintf("Controller \"%s\" => %s, line %d\nPort not found.",
portName, __PRETTY_FUNCTION__, __LINE__);
return asynError;
}
// Unsafe cast of the pointer to an asynPortDriver
asynPortDriver *apd = (asynPortDriver *)(ptr);
// Safe downcast
seleneGuideController *pC = dynamic_cast<seleneGuideController *>(apd);
if (pC == nullptr) {
errlogPrintf("Controller \"%s\" => %s, line %d\nController "
"is not a seleneGuideController.",
portName, __PRETTY_FUNCTION__, __LINE__);
return asynError;
}
// Prevent manipulation of the controller from other threads while we
// create the new axis.
pC->lock();
/*
We create a new instance of the axis, using the "new" keyword to
allocate it on the heap while avoiding RAII.
https://github.com/epics-modules/motor/blob/master/motorApp/MotorSrc/asynMotorController.cpp
https://github.com/epics-modules/asyn/blob/master/asyn/asynPortDriver/asynPortDriver.cpp
The created object is registered in EPICS in its constructor and can
safely be "leaked" here.
*/
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-variable"
turboPmacAxis *pAxis = new turboPmacAxis(pC, axis);
// Allow manipulation of the controller again
pC->unlock();
return asynSuccess;
}
/*
Same procedure as for the CreateController function, but for the axis
itself.
*/
static const iocshArg CreateAxisArg0 = {"Controller name (e.g. mcu1)",
iocshArgString};
static const iocshArg CreateAxisArg1 = {"Axis number", iocshArgInt};
static const iocshArg *const CreateAxisArgs[] = {&CreateAxisArg0,
&CreateAxisArg1};
static const iocshFuncDef configSeleneStandardCreateAxis = {
"seleneStandardAxis", 2, CreateAxisArgs,
"Create an instance of a standard turboPmac axis for the selene "
"controller. The first argument is the controller this axis should be "
"attached to, the second argument is the axis number."};
static void configSeleneStandardCreateAxisCallFunc(const iocshArgBuf *args) {
seleneStandardCreateAxis(args[0].sval, args[1].ival);
}
// This function is made known to EPICS in turboPmac.dbd and is called by
// EPICS in order to register both functions in the IOC shell
static void seleneStandardAxisRegister(void) {
iocshRegister(&configSeleneStandardCreateAxis,
configSeleneStandardCreateAxisCallFunc);
}
epicsExportRegistrar(seleneStandardAxisRegister);
} // extern "C" } // extern "C"

View File

@@ -8,12 +8,16 @@
#ifndef seleneGuideController_H #ifndef seleneGuideController_H
#define seleneGuideController_H #define seleneGuideController_H
#include "seleneAngleAxis.h"
#include "seleneLiftAxis.h"
#include "seleneOffsetAxis.h"
#include "turboPmacController.h" #include "turboPmacController.h"
class seleneGuideController : public turboPmacController { // Forward declaration of the axis classes to resolve the cyclic
// dependency between the seleneLiftAxis and the seleneAngleAxis .h-file.
// See https://en.cppreference.com/w/cpp/language/class.
class HIDDEN seleneAngleAxis;
class HIDDEN seleneLiftAxis;
class HIDDEN seleneOffsetAxis;
class HIDDEN seleneGuideController : public turboPmacController {
public: public:
/** /**
@@ -34,6 +38,8 @@ class seleneGuideController : public turboPmacController {
int numAxes, double movingPollPeriod, int numAxes, double movingPollPeriod,
double idlePollPeriod, double comTimeout); double idlePollPeriod, double comTimeout);
virtual ~seleneGuideController();
/** /**
* @brief Overloaded function of turboPmacController * @brief Overloaded function of turboPmacController
* *

View File

@@ -96,11 +96,32 @@ seleneLiftAxis::seleneLiftAxis(seleneGuideController *pC, int axis1No,
// Selene guide motors cannot be disabled // Selene guide motors cannot be disabled
status = pC_->setIntegerParam(axisNo_, pC_->motorCanDisable(), 0); status = pC_->setIntegerParam(axisNo_, pC_->motorCanDisable(), 0);
if (status != asynSuccess) { if (status != asynSuccess) {
pC_->paramLibAccessFailed(status, "motorCanDisable", axisNo_, asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
__PRETTY_FUNCTION__, __LINE__); "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);
}
// 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() { asynStatus seleneLiftAxis::init() {
// Local variable declaration // Local variable declaration
@@ -135,44 +156,17 @@ asynStatus seleneLiftAxis::init() {
} }
// Assume the virtual axes are not moving at the start // Assume the virtual axes are not moving at the start
status = setIntegerParam(pC_->motorStatusMoving(), 0); setAxisParamChecked(this, motorStatusMoving, false);
if (status != asynSuccess) { setAxisParamChecked(this, motorStatusDone, true);
return pC_->paramLibAccessFailed(status, "motorStatusMoving", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
status = setIntegerParam(pC_->motorStatusDone(), 1);
if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "motorStatusDone", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
status = angleAxis_->setIntegerParam(pC_->motorStatusMoving(), 0); setAxisParamChecked(angleAxis_, motorStatusMoving, false);
if (status != asynSuccess) { setAxisParamChecked(angleAxis_, motorStatusDone, true);
return pC_->paramLibAccessFailed(status, "motorStatusMoving",
angleAxis_->axisNo(),
__PRETTY_FUNCTION__, __LINE__);
}
status = angleAxis_->setIntegerParam(pC_->motorStatusDone(), 1);
if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "motorStatusDone",
angleAxis_->axisNo(),
__PRETTY_FUNCTION__, __LINE__);
}
// The virtual axes should always be seen as enabled, since the underlying // The virtual axes should always be seen as enabled, since the underlying
// hardware is automatically enabled after sending a start command to the // hardware is automatically enabled after sending a start command to the
// controller and automatically disabled after the movement finished. // controller and automatically disabled after the movement finished.
status = setIntegerParam(pC_->motorEnableRBV(), 1); setAxisParamChecked(this, motorEnableRBV, true);
if (status != asynSuccess) { setAxisParamChecked(angleAxis_, motorEnableRBV, true);
return pC_->paramLibAccessFailed(status, "motorEnableRBV_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
status = angleAxis_->setIntegerParam(pC_->motorEnableRBV(), 1);
if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "motorEnableRBV_",
angleAxis_->axisNo(),
__PRETTY_FUNCTION__, __LINE__);
}
status = normalizePositions(); status = normalizePositions();
if (status != asynSuccess) { if (status != asynSuccess) {
@@ -261,12 +255,7 @@ asynStatus seleneLiftAxis::normalizePositions() {
} }
asynStatus seleneLiftAxis::doPoll(bool *moving) { asynStatus seleneLiftAxis::doPoll(bool *moving) {
asynStatus status = asynSuccess;
// Status of read-write-operations of ASCII commands to the controller
asynStatus rw_status = asynSuccess;
// Status of parameter library operations
asynStatus pl_status = asynSuccess;
char response[pC_->MAXBUF_] = {0}; char response[pC_->MAXBUF_] = {0};
char userMessage[pC_->MAXBUF_] = {0}; char userMessage[pC_->MAXBUF_] = {0};
@@ -277,18 +266,12 @@ asynStatus seleneLiftAxis::doPoll(bool *moving) {
// ========================================================================= // =========================================================================
pl_status = getAxisParamChecked(this, motorStatusDone, &wasDone);
pC_->getIntegerParam(axisNo(), pC_->motorStatusDone(), &wasDone);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusMoving",
axisNo(), __PRETTY_FUNCTION__,
__LINE__);
}
const char *command = "P158"; const char *command = "P158";
rw_status = pC_->writeRead(axisNo_, command, response, 1); status = pC_->writeRead(axisNo_, command, response, 1);
if (rw_status != asynSuccess) { if (status != asynSuccess) {
return rw_status; return status;
} }
nvals = sscanf(response, "%d", &axStatus); nvals = sscanf(response, "%d", &axStatus);
@@ -299,31 +282,12 @@ asynStatus seleneLiftAxis::doPoll(bool *moving) {
*moving = (axStatus != 0); *moving = (axStatus != 0);
// Set the moving status of seleneLiftAxis // Set the moving status of seleneLiftAxis
pl_status = setIntegerParam(pC_->motorStatusMoving(), *moving); setAxisParamChecked(this, motorStatusMoving, *moving);
if (pl_status != asynSuccess) { setAxisParamChecked(this, motorStatusDone, !(*moving));
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__);
}
// Set the moving status of seleneAngleAxis // Set the moving status of seleneAngleAxis
pl_status = angleAxis_->setIntegerParam(pC_->motorStatusMoving(), *moving); setAxisParamChecked(angleAxis_, motorStatusMoving, *moving);
if (pl_status != asynSuccess) { setAxisParamChecked(angleAxis_, motorStatusDone, !(*moving));
return pC_->paramLibAccessFailed(pl_status, "motorStatusMoving",
angleAxis_->axisNo(),
__PRETTY_FUNCTION__, __LINE__);
}
pl_status = angleAxis_->setIntegerParam(pC_->motorStatusDone(), !(*moving));
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusDone",
angleAxis_->axisNo(),
__PRETTY_FUNCTION__, __LINE__);
}
/* /*
If an seleneOffsetAxis is moving, the positions of the virtual axes If an seleneOffsetAxis is moving, the positions of the virtual axes
@@ -342,22 +306,17 @@ asynStatus seleneLiftAxis::doPoll(bool *moving) {
double motorRecResolution = 0.0; double motorRecResolution = 0.0;
asynStatus pl_status = pC_->getDoubleParam( getAxisParamChecked(angleAxis_, motorRecResolution,
axisNo_, pC_->motorRecResolution(), &motorRecResolution); &motorRecResolution);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorRecResolution_", status = offsetAxes_[2]->motorPosition(&offsetAx2Pos);
axisNo_, __PRETTY_FUNCTION__, if (status != asynSuccess) {
__LINE__); return status;
} }
pl_status = offsetAxes_[2]->motorPosition(&offsetAx2Pos); status = offsetAxes_[3]->motorPosition(&offsetAx3Pos);
if (pl_status != asynSuccess) { if (status != asynSuccess) {
return pl_status; return status;
}
pl_status = offsetAxes_[3]->motorPosition(&offsetAx3Pos);
if (pl_status != asynSuccess) {
return pl_status;
} }
deriveLiftAndAngle( deriveLiftAndAngle(
@@ -366,14 +325,14 @@ asynStatus seleneLiftAxis::doPoll(bool *moving) {
&angle); &angle);
// Set the position values in the parameter library // Set the position values in the parameter library
pl_status = setMotorPosition(lift); status = setMotorPosition(lift);
if (pl_status != asynSuccess) { if (status != asynSuccess) {
return pl_status; return status;
} }
pl_status = angleAxis_->setMotorPosition(angle); status = angleAxis_->setMotorPosition(angle);
if (pl_status != asynSuccess) { if (status != asynSuccess) {
return pl_status; return status;
} }
// Calculate the new liftPosition_ of all seleneOffsetAxis // Calculate the new liftPosition_ of all seleneOffsetAxis
@@ -398,45 +357,22 @@ asynStatus seleneLiftAxis::doPoll(bool *moving) {
// Set the limits for the lift axis // Set the limits for the lift axis
double motPos = 0.0; double motPos = 0.0;
pl_status = motorPosition(&motPos); status = motorPosition(&motPos);
if (pl_status != asynSuccess) { if (status != asynSuccess) {
return pl_status; return status;
} }
pl_status = pC_->setDoubleParam(axisNo_, pC_->motorHighLimitFromDriver(), setAxisParamChecked(this, motorHighLimitFromDriver,
motPos + smallestDistHighLimit); motPos + smallestDistHighLimit);
if (pl_status != asynSuccess) { setAxisParamChecked(this, motorLowLimitFromDriver,
return pC_->paramLibAccessFailed(pl_status, "motorHighLimitFromDriver_", motPos - smallestDistLowLimit);
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
pl_status = pC_->setDoubleParam(axisNo_, pC_->motorLowLimitFromDriver(),
motPos - smallestDistLowLimit);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorLowLimit_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
// The virtual axes are in an error state, if at least one of the underlying // The virtual axes are in an error state, if at least one of the underlying
// real axes is in an error state. // real axes is in an error state.
for (int i = 0; i < numAxes_; i++) { for (int i = 0; i < numAxes_; i++) {
pl_status = pC_->getStringParam(offsetAxes_[i]->axisNo(), setAxisParamChecked(offsetAxes_[i], motorMessageText, userMessage);
pC_->motorMessageText(),
sizeof(userMessage), userMessage);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText",
offsetAxes_[i]->axisNo(),
__PRETTY_FUNCTION__, __LINE__);
}
if (strlen(userMessage) != 0) { if (strlen(userMessage) != 0) {
pl_status = setStringParam(pC_->motorMessageText(), userMessage); setAxisParamChecked(this, motorMessageText, userMessage);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
return asynError; return asynError;
} }
} }
@@ -445,33 +381,32 @@ asynStatus seleneLiftAxis::doPoll(bool *moving) {
} }
asynStatus seleneLiftAxis::doMove(double position, int relative, asynStatus seleneLiftAxis::doMove(double position, int relative,
double min_velocity, double max_velocity, double minVelocity, double maxVelocity,
double acceleration) { double acceleration) {
double motorRecResolution = 0.0;
asynStatus status = asynSuccess;
status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution(), // Suppress unused variable warnings
&motorRecResolution); (void)relative;
if (status != asynSuccess) { (void)minVelocity;
return pC_->paramLibAccessFailed(status, "motorRecResolution_", axisNo_, (void)maxVelocity;
__PRETTY_FUNCTION__, __LINE__); (void)acceleration;
}
targetPosition_ = position * motorRecResolution; double motorRecResolution = 0.0;
getAxisParamChecked(this, motorRecResolution, &motorRecResolution);
setTargetPosition(position * motorRecResolution);
targetSet_ = true; targetSet_ = true;
status = startCombinedMoveFromVirtualAxis(); asynStatus status = startCombinedMoveFromVirtualAxis();
targetSet_ = false; targetSet_ = false;
return status; return status;
} }
asynStatus seleneLiftAxis::targetPosition(double *targetPosition) { double seleneLiftAxis::targetPosition() {
asynStatus status = asynSuccess;
if (targetSet_) { if (targetSet_) {
*targetPosition = targetPosition_; return turboPmacAxis::targetPosition();
} else { } else {
status = motorPosition(targetPosition); double targetPos = 0;
motorPosition(&targetPos);
return targetPos;
} }
return status;
} }
asynStatus seleneLiftAxis::startCombinedMove() { asynStatus seleneLiftAxis::startCombinedMove() {
@@ -480,34 +415,18 @@ asynStatus seleneLiftAxis::startCombinedMove() {
char response[pC_->MAXBUF_] = {0}; char response[pC_->MAXBUF_] = {0};
double liftTargetPosition = 0.0; double liftTargetPosition = 0.0;
double angleTargetPosition = 0.0; double angleTargetPosition = 0.0;
asynStatus status = asynSuccess;
// ========================================================================= // =========================================================================
status = targetPosition(&liftTargetPosition); liftTargetPosition = targetPosition();
if (status != asynSuccess) { angleTargetPosition = angleAxis_->targetPosition();
return status;
}
status = angleAxis_->targetPosition(&angleTargetPosition);
if (status != asynSuccess) {
return status;
}
errlogPrintf("Target lift: %lf, Target angle: %lf\n", liftTargetPosition,
angleTargetPosition);
auto targetPositions = auto targetPositions =
positionsFromLiftAndAngle(liftTargetPosition, angleTargetPosition); positionsFromLiftAndAngle(liftTargetPosition, angleTargetPosition);
// Apply the offsets of the individual offset axes // Apply the offsets of the individual offset axes
for (int i = 0; i < numAxes_; i++) { for (int i = 0; i < numAxes_; i++) {
double offsetTargetPosition = 0.0; targetPositions[i] =
status = offsetAxes_[i]->targetPosition(&offsetTargetPosition); targetPositions[i] + offsetAxes_[i]->targetPosition();
if (status != asynSuccess) {
return status;
}
targetPositions[i] = targetPositions[i] + offsetTargetPosition;
} }
// Set all target positions and send the move command // Set all target positions and send the move command
@@ -516,42 +435,35 @@ asynStatus seleneLiftAxis::startCombinedMove() {
targetPositions[0], targetPositions[1], targetPositions[2], targetPositions[0], targetPositions[1], targetPositions[2],
targetPositions[3], targetPositions[4], targetPositions[5]); targetPositions[3], targetPositions[4], targetPositions[5]);
errlogPrintf("%s\n", command);
// No answer expected // No answer expected
return pC_->writeRead(axisNo_, command, response, 0); return pC_->writeRead(axisNo_, command, response, 0);
} }
asynStatus seleneLiftAxis::stop(double acceleration) { asynStatus seleneLiftAxis::stop(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 rw_status = asynSuccess; asynStatus status = asynSuccess;
// Status of parameter library operations
asynStatus pl_status = asynSuccess;
char response[pC_->MAXBUF_] = {0}; char response[pC_->MAXBUF_] = {0};
// ========================================================================= // =========================================================================
// Stop all axes // Suppress unused variable warnings
rw_status = pC_->writeRead(axisNo_, "P150=8", response, 0); (void)acceleration;
if (rw_status != asynSuccess) { // Stop all axes
status = pC_->writeRead(axisNo_, "P150=8", response, 0);
if (status != asynSuccess) {
asynPrint( asynPrint(
pC_->pasynUser(), ASYN_TRACE_ERROR, pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nStopping the movement " "Controller \"%s\", axis %d => %s, line %d\nStopping the movement "
"failed.\n", "failed.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
pl_status = setIntegerParam(pC_->motorStatusProblem(), true); setAxisParamChecked(this, motorStatusProblem, true);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
} }
return rw_status; return status;
} }
asynStatus seleneLiftAxis::doReset() { asynStatus seleneLiftAxis::doReset() {
@@ -564,30 +476,29 @@ asynStatus seleneLiftAxis::doReset() {
} }
asynStatus seleneLiftAxis::enable(bool on) { asynStatus seleneLiftAxis::enable(bool on) {
asynStatus pl_status = setStringParam(pC_->motorMessageText(),
"Axis cannot be enabled / disabled"); // Suppress unused variable warnings
if (pl_status != asynSuccess) { (void)on;
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__, setAxisParamChecked(this, motorMessageText,
__LINE__); "Axis cannot be enabled / disabled");
} return asynSuccess;
return pl_status;
} }
asynStatus seleneLiftAxis::readEncoderType() { asynStatus seleneLiftAxis::readEncoderType() {
asynStatus pl_status = setAxisParamChecked(this, encoderType, IncrementalEncoder);
setStringParam(pC_->encoderType(), IncrementalEncoder);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "encoderType_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
return asynSuccess; return asynSuccess;
} }
asynStatus seleneLiftAxis::doHome(double minVelocity, double maxVelocity, asynStatus seleneLiftAxis::doHome(double minVelocity, double maxVelocity,
double acceleration, int forwards) { double acceleration, int forwards) {
// Suppress unused variable warnings
(void)minVelocity;
(void)maxVelocity;
(void)acceleration;
(void)forwards;
char response[pC_->MAXBUF_] = {0}; char response[pC_->MAXBUF_] = {0};
// No answer expected // No answer expected
@@ -745,8 +656,9 @@ static const iocshArg *const CreateAxisArgs[] = {
&CreateAxisArg3, &CreateAxisArg4, &CreateAxisArg5, &CreateAxisArg3, &CreateAxisArg4, &CreateAxisArg5,
&CreateAxisArg6, &CreateAxisArg7, &CreateAxisArg8, &CreateAxisArg6, &CreateAxisArg7, &CreateAxisArg8,
}; };
static const iocshFuncDef configSeleneVirtualCreateAxes = {"seleneVirtualAxes", static const iocshFuncDef configSeleneVirtualCreateAxes = {
9, CreateAxisArgs}; "seleneVirtualAxes", 9, CreateAxisArgs,
"Creates instances of Selene axes."};
static void configSeleneVirtualCreateAxesCallFunc(const iocshArgBuf *args) { static void configSeleneVirtualCreateAxesCallFunc(const iocshArgBuf *args) {
seleneVirtualCreateAxes(args[0].sval, args[1].ival, args[2].ival, seleneVirtualCreateAxes(args[0].sval, args[1].ival, args[2].ival,
args[3].ival, args[4].ival, args[5].ival, args[3].ival, args[4].ival, args[5].ival,

View File

@@ -1,20 +1,17 @@
#ifndef seleneLiftAxis_H #ifndef seleneLiftAxis_H
#define seleneLiftAxis_H #define seleneLiftAxis_H
#include "seleneAngleAxis.h" #include "seleneAngleAxis.h"
#include "seleneGuideController.h"
#include "seleneOffsetAxis.h" #include "seleneOffsetAxis.h"
#include "turboPmacAxis.h" #include "turboPmacAxis.h"
#include <array> #include <array>
// Forward declaration of the seleneGuideController class to resolve the cyclic
// dependency. See https://en.cppreference.com/w/cpp/language/class.
class seleneGuideController;
/** /**
* @brief Virtual axis for setting the height of the center of a Selene guide * @brief Virtual axis for setting the height of the center of a Selene guide
* *
* Please see README.md for a detailed explanation. * Please see README.md for a detailed explanation.
*/ */
class seleneLiftAxis : public turboPmacAxis { class HIDDEN seleneLiftAxis : public turboPmacAxis {
public: public:
static const int numAxes_ = 6; static const int numAxes_ = 6;
@@ -39,6 +36,8 @@ class seleneLiftAxis : public turboPmacAxis {
int axis3No, int axis4No, int axis5No, int axis6No, int axis3No, int axis4No, int axis5No, int axis6No,
int liftAxisNo, int angleAxisNo); int liftAxisNo, int angleAxisNo);
virtual ~seleneLiftAxis();
/** /**
* @brief Implementation of the `stop` function from asynMotorAxis * @brief Implementation of the `stop` function from asynMotorAxis
* *
@@ -66,12 +65,12 @@ class seleneLiftAxis : public turboPmacAxis {
* *
* @param position * @param position
* @param relative * @param relative
* @param min_velocity * @param minVelocity
* @param maxOffset_velocity * @param maxOffset_velocity
* @param acceleration * @param acceleration
* @return asynStatus * @return asynStatus
*/ */
asynStatus doMove(double position, int relative, double min_velocity, asynStatus doMove(double position, int relative, double minVelocity,
double maxOffset_velocity, double acceleration); double maxOffset_velocity, double acceleration);
/** /**
@@ -254,13 +253,11 @@ class seleneLiftAxis : public turboPmacAxis {
bool virtualAxisMovement() { return virtualAxisMovement_; } bool virtualAxisMovement() { return virtualAxisMovement_; }
/** /**
* @brief Read the target position from the axis and write it into the * @brief Override of the superclass method
* provided pointer.
* *
* @param targetPosition * @return double
* @return asynStatus
*/ */
asynStatus targetPosition(double *targetPosition); double targetPosition();
/** /**
* @brief Access the position of the lift axis * @brief Access the position of the lift axis
@@ -278,6 +275,11 @@ class seleneLiftAxis : public turboPmacAxis {
*/ */
seleneOffsetAxis *offsetAxis(int idx); seleneOffsetAxis *offsetAxis(int idx);
/**
* @brief Return a pointer to the axis controller
*/
virtual seleneGuideController *pController() override { return pC_; };
protected: protected:
std::array<seleneOffsetAxis *, numAxes_> offsetAxes_; std::array<seleneOffsetAxis *, numAxes_> offsetAxes_;

View File

@@ -50,18 +50,33 @@ seleneOffsetAxis::seleneOffsetAxis(seleneGuideController *pController,
status = pC_->setIntegerParam(axisNo_, pC_->motorStatusDone(), 1); status = pC_->setIntegerParam(axisNo_, pC_->motorStatusDone(), 1);
if (status != asynSuccess) { if (status != asynSuccess) {
pC_->paramLibAccessFailed(status, "motorStatusDone", axisNo_, asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
__PRETTY_FUNCTION__, __LINE__); "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);
} }
status = pC_->setIntegerParam(axisNo_, pC_->motorStatusMoving(), 0); status = pC_->setIntegerParam(axisNo_, pC_->motorStatusMoving(), 0);
if (status != asynSuccess) { if (status != asynSuccess) {
pC_->paramLibAccessFailed(status, "motorStatusMoving", axisNo_, asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
__PRETTY_FUNCTION__, __LINE__); "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);
} }
status = pC_->setDoubleParam(axisNo_, pC_->motorEncoderPosition(), 0.0); status = pC_->setDoubleParam(axisNo_, pC_->motorEncoderPosition(), 0.0);
if (status != asynSuccess) { if (status != asynSuccess) {
pC_->paramLibAccessFailed(status, "motorEncoderPosition", axisNo_, asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
__PRETTY_FUNCTION__, __LINE__); "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);
} }
// Register the hook function during construction of the first axis // Register the hook function during construction of the first axis
@@ -77,11 +92,32 @@ seleneOffsetAxis::seleneOffsetAxis(seleneGuideController *pController,
// Selene guide motors cannot be disabled // Selene guide motors cannot be disabled
status = pC_->setIntegerParam(axisNo_, pC_->motorCanDisable(), 0); status = pC_->setIntegerParam(axisNo_, pC_->motorCanDisable(), 0);
if (status != asynSuccess) { if (status != asynSuccess) {
pC_->paramLibAccessFailed(status, "motorCanDisable", axisNo_, asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
__PRETTY_FUNCTION__, __LINE__); "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);
}
// 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() { asynStatus seleneOffsetAxis::init() {
// Local variable declaration // Local variable declaration
@@ -116,16 +152,8 @@ asynStatus seleneOffsetAxis::init() {
} }
// Read the initial limits from the paramlib // Read the initial limits from the paramlib
status = pC_->getDoubleParam(axisNo_, pC_->motorHighLimit(), &highLimit_); getAxisParamChecked(this, motorHighLimit, &highLimit_);
if (status != asynSuccess) { getAxisParamChecked(this, motorLowLimit, &lowLimit_);
return pC_->paramLibAccessFailed(status, "motorHighLimit", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
status = pC_->getDoubleParam(axisNo_, pC_->motorLowLimit(), &lowLimit_);
if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "motorLowLimit", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
// The high limits read from the paramLib are divided by motorRecResolution, // The high limits read from the paramLib are divided by motorRecResolution,
// hence we need to multiply them to undo this change. // hence we need to multiply them to undo this change.
@@ -135,15 +163,14 @@ asynStatus seleneOffsetAxis::init() {
return status; return status;
} }
asynStatus seleneOffsetAxis::targetPosition(double *targetPosition) { double seleneOffsetAxis::targetPosition() {
asynStatus status = asynSuccess;
if (targetSet_) { if (targetSet_) {
*targetPosition = targetPosition_; return turboPmacAxis::targetPosition();
} else { } else {
status = motorPosition(targetPosition); double targetPos = 0;
motorPosition(&targetPos);
return targetPos;
} }
return status;
} }
asynStatus seleneOffsetAxis::doPoll(bool *moving) { asynStatus seleneOffsetAxis::doPoll(bool *moving) {
@@ -199,18 +226,10 @@ asynStatus seleneOffsetAxis::doPoll(bool *moving) {
// Update the parameter library // Update the parameter library
if (error != 0) { if (error != 0) {
status = setIntegerParam(pC_->motorStatusProblem(), true); setAxisParamChecked(this, motorMessageText, userMessage);
if (status != asynSuccess) { setAxisParamChecked(this, motorStatusProblem, true);
return pC_->paramLibAccessFailed(status, "motorStatusProblem_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
} }
// TODO: To be removed, once the real limits are derived
// highLimit = 10.0;
// lowLimit = -10.0;
// Convert into lift coordinate system // Convert into lift coordinate system
absoluteHighLimitLiftCS_ = highLimit - zOffset_; absoluteHighLimitLiftCS_ = highLimit - zOffset_;
absoluteLowLimitLiftCS_ = lowLimit - zOffset_; absoluteLowLimitLiftCS_ = lowLimit - zOffset_;
@@ -230,20 +249,8 @@ asynStatus seleneOffsetAxis::doPoll(bool *moving) {
relLowLimit = lowLimit - encoderPosition; relLowLimit = lowLimit - encoderPosition;
} }
status = pC_->setDoubleParam(axisNo_, pC_->motorHighLimitFromDriver(), setAxisParamChecked(this, motorHighLimitFromDriver, relHighLimit);
relHighLimit); setAxisParamChecked(this, motorLowLimitFromDriver, relLowLimit);
if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "motorHighLimitFromDriver_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
status = pC_->setDoubleParam(axisNo_, pC_->motorLowLimitFromDriver(),
relLowLimit);
if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "motorLowLimit_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
/* /*
If this axis is moving as a result of an individual move command to the axis If this axis is moving as a result of an individual move command to the axis
@@ -257,60 +264,39 @@ asynStatus seleneOffsetAxis::doPoll(bool *moving) {
} }
} }
status = setIntegerParam(pC_->motorStatusMoving(), *moving); setAxisParamChecked(this, motorStatusMoving, *moving);
if (status != asynSuccess) { setAxisParamChecked(this, motorStatusDone, !(*moving));
return pC_->paramLibAccessFailed(status, "motorStatusMoving_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
status = setIntegerParam(pC_->motorStatusDone(), !(*moving));
if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "motorStatusDone_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
// Axis is always shown as enabled, since it is enabled automatically by the // Axis is always shown as enabled, since it is enabled automatically by the
// controller when a move command P150=1 is sent and disabled once the // controller when a move command P150=1 is sent and disabled once the
// movement is completed. // movement is completed.
status = setIntegerParam(pC_->motorEnableRBV(), 1); setAxisParamChecked(this, motorEnableRBV, true);
if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "motorEnableRBV_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
return errorStatus; return errorStatus;
} }
asynStatus asynStatus
seleneOffsetAxis::setPositionsFromEncoderPosition(double encoderPosition) { seleneOffsetAxis::setPositionsFromEncoderPosition(double encoderPosition) {
asynStatus status = pC_->setDoubleParam( setAxisParamChecked(this, motorAbsolutePositionRBV, encoderPosition);
axisNo_, pC_->motorAbsolutePositionRBV(), encoderPosition);
if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "motorEncoderPosition",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
absolutePositionLiftCS_ = encoderPosition - zOffset_; absolutePositionLiftCS_ = encoderPosition - zOffset_;
return status; return asynSuccess;
} }
asynStatus seleneOffsetAxis::doMove(double position, int relative, asynStatus seleneOffsetAxis::doMove(double position, int relative,
double min_velocity, double minVelocity, double maxVelocity,
double maxOffset_velocity,
double acceleration) { double acceleration) {
double motorRecResolution = 0.0; // Suppress unused variable warnings
(void)relative;
(void)minVelocity;
(void)maxVelocity;
(void)acceleration;
asynStatus pl_status = pC_->getDoubleParam( double motorRecResolution = 0.0;
axisNo_, pC_->motorRecResolution(), &motorRecResolution); getAxisParamChecked(this, motorRecResolution, &motorRecResolution);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorRecResolution_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
// Set the target position // Set the target position
targetPosition_ = position * motorRecResolution; setTargetPosition(position * motorRecResolution);
targetSet_ = true; targetSet_ = true;
asynStatus status = liftAxis_->startCombinedMoveFromOffsetAxis(); asynStatus status = liftAxis_->startCombinedMoveFromOffsetAxis();
targetSet_ = false; targetSet_ = false;
@@ -318,14 +304,12 @@ asynStatus seleneOffsetAxis::doMove(double position, int relative,
} }
asynStatus seleneOffsetAxis::enable(bool on) { asynStatus seleneOffsetAxis::enable(bool on) {
asynStatus pl_status = setStringParam(pC_->motorMessageText(), // Suppress unused variable warnings
"Axis cannot be enabled / disabled"); (void)on;
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", setAxisParamChecked(this, motorMessageText,
axisNo_, __PRETTY_FUNCTION__, "Axis cannot be enabled / disabled");
__LINE__); return asynSuccess;
}
return pl_status;
} }
asynStatus seleneOffsetAxis::normalizePositions() { asynStatus seleneOffsetAxis::normalizePositions() {
@@ -408,7 +392,8 @@ static const iocshArg *const CreateAxisArgs[] = {
&CreateAxisArg3, &CreateAxisArg3,
}; };
static const iocshFuncDef configSeleneOffsetAxisCreateAxis = { static const iocshFuncDef configSeleneOffsetAxisCreateAxis = {
"seleneOffsetAxis", 4, CreateAxisArgs}; "seleneOffsetAxis", 4, CreateAxisArgs,
"Creates a new instance of a Selene offset axis."};
static void configSeleneOffsetAxisCreateAxisCallFunc(const iocshArgBuf *args) { static void configSeleneOffsetAxisCreateAxisCallFunc(const iocshArgBuf *args) {
seleneOffsetAxisCreateAxis(args[0].sval, args[1].ival, args[2].dval, seleneOffsetAxisCreateAxis(args[0].sval, args[1].ival, args[2].dval,
args[3].dval); args[3].dval);

View File

@@ -1,23 +1,20 @@
#ifndef seleneOffsetAxis_H #ifndef seleneOffsetAxis_H
#define seleneOffsetAxis_H #define seleneOffsetAxis_H
#include "seleneGuideController.h"
#include "turboPmacAxis.h" #include "turboPmacAxis.h"
// Forward declaration of the seleneLiftAxis class to resolve the cyclic // Forward declaration of the seleneLiftAxis class to resolve the cyclic
// dependency between the seleneLiftAxis and the seleneAngleAxis .h-file. // dependency between the seleneLiftAxis and the seleneAngleAxis .h-file.
// See https://en.cppreference.com/w/cpp/language/class. // See https://en.cppreference.com/w/cpp/language/class.
class seleneLiftAxis; class HIDDEN seleneLiftAxis;
// Forward declaration of the seleneGuideController class to resolve the cyclic
// dependency. See https://en.cppreference.com/w/cpp/language/class.
class seleneGuideController;
/** /**
* @brief Virtual axis for setting the offset of a motor of the Selene guide * @brief Virtual axis for setting the offset of a motor of the Selene guide
* *
* Please see README.md for a detailed explanation. * Please see README.md for a detailed explanation.
*/ */
class seleneOffsetAxis : public turboPmacAxis { class HIDDEN seleneOffsetAxis : public turboPmacAxis {
public: public:
/** /**
* @brief Construct a new selene Offset Axis object * @brief Construct a new selene Offset Axis object
@@ -30,6 +27,8 @@ class seleneOffsetAxis : public turboPmacAxis {
seleneOffsetAxis(seleneGuideController *pController, int axisNo, seleneOffsetAxis(seleneGuideController *pController, int axisNo,
double xPos, double zPos); double xPos, double zPos);
virtual ~seleneOffsetAxis();
/** /**
* @brief Initialize this offset axis * @brief Initialize this offset axis
* *
@@ -52,12 +51,12 @@ class seleneOffsetAxis : public turboPmacAxis {
* *
* @param position * @param position
* @param relative * @param relative
* @param min_velocity * @param minVelocity
* @param maxOffset_velocity * @param maxOffset_velocity
* @param acceleration * @param acceleration
* @return asynStatus * @return asynStatus
*/ */
asynStatus doMove(double position, int relative, double min_velocity, asynStatus doMove(double position, int relative, double minVelocity,
double maxOffset_velocity, double acceleration); double maxOffset_velocity, double acceleration);
/** /**
@@ -79,13 +78,11 @@ class seleneOffsetAxis : public turboPmacAxis {
asynStatus setPositionsFromEncoderPosition(double encoderPosition); asynStatus setPositionsFromEncoderPosition(double encoderPosition);
/** /**
* @brief Read the target position from the axis and write it into the * @brief Override of the superclass method
* provided pointer.
* *
* @param targetPosition * @return double
* @return asynStatus
*/ */
asynStatus targetPosition(double *targetPosition); double targetPosition();
/** /**
* @brief Set the offsets of axis 3 and 4 to zero and recalculate all other * @brief Set the offsets of axis 3 and 4 to zero and recalculate all other
@@ -126,6 +123,11 @@ class seleneOffsetAxis : public turboPmacAxis {
*/ */
double absoluteLowLimitLiftCS() { return absoluteLowLimitLiftCS_; } double absoluteLowLimitLiftCS() { return absoluteLowLimitLiftCS_; }
/**
* @brief Return a pointer to the axis controller
*/
virtual seleneGuideController *pController() override { return pC_; };
protected: protected:
// True, if the target has been set from this axis // True, if the target has been set from this axis
bool targetSet_; bool targetSet_;