Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f5287609e5 | |||
| e322dd09c1 | |||
| 754fc16cd3 | |||
| a7cb1cd05f | |||
| f9d5d1aaee | |||
| ef81f38017 | |||
| 6ee554f9e5 | |||
| 7bd3adbfc3 | |||
| bff7ad79df | |||
| 70029b12b2 | |||
| ac5ab3855e | |||
| 2c9de618ec | |||
| b834dc3f8d |
3
Makefile
3
Makefile
@@ -42,4 +42,5 @@ DBDS += turboPmac/sinqMotor/src/sinqMotor.dbd
|
||||
DBDS += turboPmac/src/turboPmac.dbd
|
||||
DBDS += src/seleneGuide.dbd
|
||||
|
||||
USR_CFLAGS += -Wall -Wextra -Wunused-result -Wextra -Werror -fvisibility=hidden# -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
|
||||
@@ -44,7 +44,7 @@ Two additional PVs are provided in `db/seleneGuide.db`:
|
||||
### Usage in IOC shell
|
||||
|
||||
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.
|
||||
- `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);
|
||||
|
||||
# These are two "normal" PMAC axes which work the same way they would with a turboPmacController.
|
||||
turboPmacAxis("$(NAME)",7);
|
||||
turboPmacAxis("$(NAME)",8);
|
||||
# Using the wrapper seleneStandardAxis ensures compatibility to the seleneGuideController.
|
||||
seleneStandardAxis("$(NAME)",7);
|
||||
seleneStandardAxis("$(NAME)",8);
|
||||
|
||||
# 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
|
||||
|
||||
@@ -50,8 +50,15 @@ asynStatus seleneAngleAxis::stop(double acceleration) {
|
||||
}
|
||||
|
||||
asynStatus seleneAngleAxis::doMove(double position, int relative,
|
||||
double min_velocity, double max_velocity,
|
||||
double minVelocity, double maxVelocity,
|
||||
double acceleration) {
|
||||
|
||||
// Suppress unused variable warnings
|
||||
(void)relative;
|
||||
(void)minVelocity;
|
||||
(void)maxVelocity;
|
||||
(void)acceleration;
|
||||
|
||||
double motorRecResolution = 0.0;
|
||||
|
||||
getAxisParamChecked(this, motorRecResolution, &motorRecResolution);
|
||||
|
||||
@@ -48,12 +48,12 @@ class HIDDEN seleneAngleAxis : public turboPmacAxis {
|
||||
*
|
||||
* @param position
|
||||
* @param relative
|
||||
* @param min_velocity
|
||||
* @param minVelocity
|
||||
* @param maxOffset_velocity
|
||||
* @param acceleration
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus doMove(double position, int relative, double min_velocity,
|
||||
asynStatus doMove(double position, int relative, double minVelocity,
|
||||
double maxOffset_velocity, double acceleration);
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
registrar(seleneVirtualAxesRegister)
|
||||
registrar(seleneOffsetAxisRegister)
|
||||
registrar(seleneStandardAxisRegister)
|
||||
registrar(seleneGuideControllerRegister)
|
||||
@@ -23,7 +23,8 @@ seleneGuideController::seleneGuideController(
|
||||
const char *portName, const char *ipPortConfigName, int numAxes,
|
||||
double movingPollPeriod, double idlePollPeriod, double comTimeout)
|
||||
: turboPmacController(portName, ipPortConfigName, numAxes, movingPollPeriod,
|
||||
idlePollPeriod, NUM_seleneGuide_DRIVER_PARAMS)
|
||||
idlePollPeriod, comTimeout,
|
||||
NUM_seleneGuide_DRIVER_PARAMS)
|
||||
|
||||
{
|
||||
|
||||
@@ -145,7 +146,8 @@ static const iocshArg *const CreateControllerArgs[] = {
|
||||
&CreateControllerArg0, &CreateControllerArg1, &CreateControllerArg2,
|
||||
&CreateControllerArg3, &CreateControllerArg4, &CreateControllerArg5};
|
||||
static const iocshFuncDef configSeleneGuideCreateController = {
|
||||
"seleneGuideController", 6, CreateControllerArgs};
|
||||
"seleneGuideController", 6, CreateControllerArgs,
|
||||
"Create a new instance of a Selene controller."};
|
||||
static void configSeleneGuideCreateControllerCallFunc(const iocshArgBuf *args) {
|
||||
seleneGuideCreateController(args[0].sval, args[1].sval, args[2].ival,
|
||||
args[3].dval, args[4].dval, args[5].dval);
|
||||
@@ -159,4 +161,97 @@ static void seleneGuideControllerRegister(void) {
|
||||
}
|
||||
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"
|
||||
|
||||
@@ -381,8 +381,15 @@ asynStatus seleneLiftAxis::doPoll(bool *moving) {
|
||||
}
|
||||
|
||||
asynStatus seleneLiftAxis::doMove(double position, int relative,
|
||||
double min_velocity, double max_velocity,
|
||||
double minVelocity, double maxVelocity,
|
||||
double acceleration) {
|
||||
|
||||
// Suppress unused variable warnings
|
||||
(void)relative;
|
||||
(void)minVelocity;
|
||||
(void)maxVelocity;
|
||||
(void)acceleration;
|
||||
|
||||
double motorRecResolution = 0.0;
|
||||
getAxisParamChecked(this, motorRecResolution, &motorRecResolution);
|
||||
setTargetPosition(position * motorRecResolution);
|
||||
@@ -440,6 +447,9 @@ asynStatus seleneLiftAxis::stop(double acceleration) {
|
||||
|
||||
// =========================================================================
|
||||
|
||||
// Suppress unused variable warnings
|
||||
(void)acceleration;
|
||||
|
||||
// Stop all axes
|
||||
status = pC_->writeRead(axisNo_, "P150=8", response, 0);
|
||||
|
||||
@@ -466,6 +476,10 @@ asynStatus seleneLiftAxis::doReset() {
|
||||
}
|
||||
|
||||
asynStatus seleneLiftAxis::enable(bool on) {
|
||||
|
||||
// Suppress unused variable warnings
|
||||
(void)on;
|
||||
|
||||
setAxisParamChecked(this, motorMessageText,
|
||||
"Axis cannot be enabled / disabled");
|
||||
return asynSuccess;
|
||||
@@ -479,6 +493,12 @@ asynStatus seleneLiftAxis::readEncoderType() {
|
||||
asynStatus seleneLiftAxis::doHome(double minVelocity, double maxVelocity,
|
||||
double acceleration, int forwards) {
|
||||
|
||||
// Suppress unused variable warnings
|
||||
(void)minVelocity;
|
||||
(void)maxVelocity;
|
||||
(void)acceleration;
|
||||
(void)forwards;
|
||||
|
||||
char response[pC_->MAXBUF_] = {0};
|
||||
|
||||
// No answer expected
|
||||
@@ -636,8 +656,9 @@ static const iocshArg *const CreateAxisArgs[] = {
|
||||
&CreateAxisArg3, &CreateAxisArg4, &CreateAxisArg5,
|
||||
&CreateAxisArg6, &CreateAxisArg7, &CreateAxisArg8,
|
||||
};
|
||||
static const iocshFuncDef configSeleneVirtualCreateAxes = {"seleneVirtualAxes",
|
||||
9, CreateAxisArgs};
|
||||
static const iocshFuncDef configSeleneVirtualCreateAxes = {
|
||||
"seleneVirtualAxes", 9, CreateAxisArgs,
|
||||
"Creates instances of Selene axes."};
|
||||
static void configSeleneVirtualCreateAxesCallFunc(const iocshArgBuf *args) {
|
||||
seleneVirtualCreateAxes(args[0].sval, args[1].ival, args[2].ival,
|
||||
args[3].ival, args[4].ival, args[5].ival,
|
||||
|
||||
@@ -65,12 +65,12 @@ class HIDDEN seleneLiftAxis : public turboPmacAxis {
|
||||
*
|
||||
* @param position
|
||||
* @param relative
|
||||
* @param min_velocity
|
||||
* @param minVelocity
|
||||
* @param maxOffset_velocity
|
||||
* @param acceleration
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus doMove(double position, int relative, double min_velocity,
|
||||
asynStatus doMove(double position, int relative, double minVelocity,
|
||||
double maxOffset_velocity, double acceleration);
|
||||
|
||||
/**
|
||||
|
||||
@@ -283,10 +283,15 @@ seleneOffsetAxis::setPositionsFromEncoderPosition(double encoderPosition) {
|
||||
}
|
||||
|
||||
asynStatus seleneOffsetAxis::doMove(double position, int relative,
|
||||
double min_velocity,
|
||||
double maxOffset_velocity,
|
||||
double minVelocity, double maxVelocity,
|
||||
double acceleration) {
|
||||
|
||||
// Suppress unused variable warnings
|
||||
(void)relative;
|
||||
(void)minVelocity;
|
||||
(void)maxVelocity;
|
||||
(void)acceleration;
|
||||
|
||||
double motorRecResolution = 0.0;
|
||||
getAxisParamChecked(this, motorRecResolution, &motorRecResolution);
|
||||
|
||||
@@ -299,6 +304,9 @@ asynStatus seleneOffsetAxis::doMove(double position, int relative,
|
||||
}
|
||||
|
||||
asynStatus seleneOffsetAxis::enable(bool on) {
|
||||
// Suppress unused variable warnings
|
||||
(void)on;
|
||||
|
||||
setAxisParamChecked(this, motorMessageText,
|
||||
"Axis cannot be enabled / disabled");
|
||||
return asynSuccess;
|
||||
@@ -384,7 +392,8 @@ static const iocshArg *const CreateAxisArgs[] = {
|
||||
&CreateAxisArg3,
|
||||
};
|
||||
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) {
|
||||
seleneOffsetAxisCreateAxis(args[0].sval, args[1].ival, args[2].dval,
|
||||
args[3].dval);
|
||||
|
||||
@@ -51,12 +51,12 @@ class HIDDEN seleneOffsetAxis : public turboPmacAxis {
|
||||
*
|
||||
* @param position
|
||||
* @param relative
|
||||
* @param min_velocity
|
||||
* @param minVelocity
|
||||
* @param maxOffset_velocity
|
||||
* @param acceleration
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus doMove(double position, int relative, double min_velocity,
|
||||
asynStatus doMove(double position, int relative, double minVelocity,
|
||||
double maxOffset_velocity, double acceleration);
|
||||
|
||||
/**
|
||||
|
||||
Submodule turboPmac updated: 1ee483f8e9...1d662ecd43
Reference in New Issue
Block a user