11 Commits
1.2.3 ... 1.3.3

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