Updated turboPmac version and added constructor for standard axis

This commit is contained in:
2026-01-23 11:16:19 +01:00
parent 2c9de618ec
commit ac5ab3855e
4 changed files with 99 additions and 4 deletions

View File

@@ -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

View File

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

View File

@@ -161,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"