Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 += turboPmac/src/turboPmac.dbd
|
||||||
DBDS += src/seleneGuide.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
|
### 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
|
||||||
|
|||||||
@@ -50,8 +50,15 @@ asynStatus seleneAngleAxis::stop(double 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;
|
||||||
|
|
||||||
getAxisParamChecked(this, motorRecResolution, &motorRecResolution);
|
getAxisParamChecked(this, motorRecResolution, &motorRecResolution);
|
||||||
|
|||||||
@@ -48,12 +48,12 @@ class HIDDEN 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);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
registrar(seleneVirtualAxesRegister)
|
registrar(seleneVirtualAxesRegister)
|
||||||
registrar(seleneOffsetAxisRegister)
|
registrar(seleneOffsetAxisRegister)
|
||||||
|
registrar(seleneStandardAxisRegister)
|
||||||
registrar(seleneGuideControllerRegister)
|
registrar(seleneGuideControllerRegister)
|
||||||
@@ -23,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)
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -145,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);
|
||||||
@@ -159,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"
|
||||||
|
|||||||
@@ -381,8 +381,15 @@ 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) {
|
||||||
|
|
||||||
|
// Suppress unused variable warnings
|
||||||
|
(void)relative;
|
||||||
|
(void)minVelocity;
|
||||||
|
(void)maxVelocity;
|
||||||
|
(void)acceleration;
|
||||||
|
|
||||||
double motorRecResolution = 0.0;
|
double motorRecResolution = 0.0;
|
||||||
getAxisParamChecked(this, motorRecResolution, &motorRecResolution);
|
getAxisParamChecked(this, motorRecResolution, &motorRecResolution);
|
||||||
setTargetPosition(position * motorRecResolution);
|
setTargetPosition(position * motorRecResolution);
|
||||||
@@ -440,6 +447,9 @@ asynStatus seleneLiftAxis::stop(double acceleration) {
|
|||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|
||||||
|
// Suppress unused variable warnings
|
||||||
|
(void)acceleration;
|
||||||
|
|
||||||
// Stop all axes
|
// Stop all axes
|
||||||
status = pC_->writeRead(axisNo_, "P150=8", response, 0);
|
status = pC_->writeRead(axisNo_, "P150=8", response, 0);
|
||||||
|
|
||||||
@@ -466,6 +476,10 @@ asynStatus seleneLiftAxis::doReset() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
asynStatus seleneLiftAxis::enable(bool on) {
|
asynStatus seleneLiftAxis::enable(bool on) {
|
||||||
|
|
||||||
|
// Suppress unused variable warnings
|
||||||
|
(void)on;
|
||||||
|
|
||||||
setAxisParamChecked(this, motorMessageText,
|
setAxisParamChecked(this, motorMessageText,
|
||||||
"Axis cannot be enabled / disabled");
|
"Axis cannot be enabled / disabled");
|
||||||
return asynSuccess;
|
return asynSuccess;
|
||||||
@@ -479,6 +493,12 @@ asynStatus seleneLiftAxis::readEncoderType() {
|
|||||||
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
|
||||||
@@ -636,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,
|
||||||
|
|||||||
@@ -65,12 +65,12 @@ class HIDDEN 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);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -283,10 +283,15 @@ seleneOffsetAxis::setPositionsFromEncoderPosition(double encoderPosition) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
|
|
||||||
|
// Suppress unused variable warnings
|
||||||
|
(void)relative;
|
||||||
|
(void)minVelocity;
|
||||||
|
(void)maxVelocity;
|
||||||
|
(void)acceleration;
|
||||||
|
|
||||||
double motorRecResolution = 0.0;
|
double motorRecResolution = 0.0;
|
||||||
getAxisParamChecked(this, motorRecResolution, &motorRecResolution);
|
getAxisParamChecked(this, motorRecResolution, &motorRecResolution);
|
||||||
|
|
||||||
@@ -299,6 +304,9 @@ asynStatus seleneOffsetAxis::doMove(double position, int relative,
|
|||||||
}
|
}
|
||||||
|
|
||||||
asynStatus seleneOffsetAxis::enable(bool on) {
|
asynStatus seleneOffsetAxis::enable(bool on) {
|
||||||
|
// Suppress unused variable warnings
|
||||||
|
(void)on;
|
||||||
|
|
||||||
setAxisParamChecked(this, motorMessageText,
|
setAxisParamChecked(this, motorMessageText,
|
||||||
"Axis cannot be enabled / disabled");
|
"Axis cannot be enabled / disabled");
|
||||||
return asynSuccess;
|
return asynSuccess;
|
||||||
@@ -384,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);
|
||||||
|
|||||||
@@ -51,12 +51,12 @@ class HIDDEN 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);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Submodule turboPmac updated: 1ee483f8e9...e10aaf978e
Reference in New Issue
Block a user