Changed dependencies to static linking.
This commit is contained in:
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[submodule "turboPmac"]
|
||||||
|
path = turboPmac
|
||||||
|
url = https://gitea.psi.ch/lin-epics-modules/turboPmac
|
24
Makefile
24
Makefile
@ -9,29 +9,31 @@ ARCH_FILTER=RHEL%
|
|||||||
# Additional module dependencies
|
# Additional module dependencies
|
||||||
REQUIRED+=motorBase
|
REQUIRED+=motorBase
|
||||||
|
|
||||||
# Specify the version of motorBase we want to build against
|
# Specify the version of motorBase we want to dynamically link against
|
||||||
motorBase_VERSION=7.2.2
|
motorBase_VERSION=7.2.2
|
||||||
|
|
||||||
# Specify the version of turboPmac we want to build against
|
|
||||||
sinqMotor_VERSION=mathis_s
|
|
||||||
|
|
||||||
# Specify the version of turboPmac we want to build against
|
|
||||||
turboPmac_VERSION=mathis_s
|
|
||||||
|
|
||||||
# These headers allow to depend on this library for derived drivers.
|
# These headers allow to depend on this library for derived drivers.
|
||||||
HEADERS += src/detectorTowerAuxiliaryAxis.h
|
HEADERS += src/detectorTowerAngleAxis.h
|
||||||
HEADERS += src/detectorTowerAxis.h
|
|
||||||
HEADERS += src/detectorTowerController.h
|
HEADERS += src/detectorTowerController.h
|
||||||
|
HEADERS += src/detectorTowerLiftAxis.h
|
||||||
|
|
||||||
# Source files to build
|
# Source files to build
|
||||||
SOURCES += src/detectorTowerAuxiliaryAxis.cpp
|
SOURCES += turboPmac/src/turboPmacAxis.cpp
|
||||||
SOURCES += src/detectorTowerAxis.cpp
|
SOURCES += turboPmac/src/turboPmacController.cpp
|
||||||
|
SOURCES += turboPmac/src/pmacAsynIPPort.c
|
||||||
|
SOURCES += src/detectorTowerAngleAxis.cpp
|
||||||
SOURCES += src/detectorTowerController.cpp
|
SOURCES += src/detectorTowerController.cpp
|
||||||
|
SOURCES += src/detectorTowerLiftAxis.cpp
|
||||||
|
|
||||||
# Store the record files
|
# Store the record files
|
||||||
|
TEMPLATES += turboPmac/sinqMotor/db/asynRecord.db
|
||||||
|
TEMPLATES += turboPmac/sinqMotor/db/sinqMotor.db
|
||||||
|
TEMPLATES += turboPmac/db/turboPmac.db
|
||||||
TEMPLATES += db/detectorTower.db
|
TEMPLATES += db/detectorTower.db
|
||||||
|
|
||||||
# This file registers the motor-specific functions in the IOC shell.
|
# This file registers the motor-specific functions in the IOC shell.
|
||||||
|
DBDS += turboPmac/sinqMotor/src/sinqMotor.dbd
|
||||||
|
DBDS += turboPmac/src/turboPmac.dbd
|
||||||
DBDS += src/detectorTower.dbd
|
DBDS += src/detectorTower.dbd
|
||||||
|
|
||||||
USR_CFLAGS += -Wall -Wextra -Weffc++ -Wunused-result -Wextra -Werror # -Wpedantic // Does not work because EPICS macros trigger warnings
|
USR_CFLAGS += -Wall -Wextra -Weffc++ -Wunused-result -Wextra -Werror # -Wpedantic // Does not work because EPICS macros trigger warnings
|
||||||
|
27
README.md
27
README.md
@ -5,14 +5,14 @@
|
|||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
This is a driver for the detector tower which is based on the Turbo PMAC driver (https://git.psi.ch/sinq-epics-modules/turboPmac). It consists of the following three objects:
|
This is a driver for the detector tower which is based on the Turbo PMAC driver (https://git.psi.ch/sinq-epics-modules/turboPmac). It consists of the following three objects:
|
||||||
- `detectorTowerAxis`: This is a virtual axis which controls multiple physical motors in order to provide a synchronized movement.
|
- `detectorTowerAngleAxis`: This is a virtual axis which controls multiple physical motors in order to provide a synchronized movement.
|
||||||
- `detectorTowerController`: This is an expanded variant of `turboPmacController` provided by the Turbo PMAC library linked above.It is needed to operate a `detectorTowerAxis`, but it can also be used to operate a "normal" `turboPmacAxis`.
|
- `detectorTowerController`: This is an expanded variant of `turboPmacController` provided by the Turbo PMAC library linked above.It is needed to operate a `detectorTowerAngleAxis`, but it can also be used to operate a "normal" `turboPmacAxis`.
|
||||||
- `detectorTowerAuxiliaryAxis`: This is an auxiliary axis type attached to the main detector axis. Multiple instances of these axes are constructed automatically when creating a `detectorTowerAxis`.
|
- `detectorTowerLiftAxis`: This is an auxiliary axis type attached to the main detector axis. Multiple instances of these axes are constructed automatically when creating a `detectorTowerAngleAxis`.
|
||||||
The header files contain detailed documentation for all public functions. The headers themselves are exported when building the library to allow other drivers to depend on this one.
|
The header files contain detailed documentation for all public functions. The headers themselves are exported when building the library to allow other drivers to depend on this one.
|
||||||
|
|
||||||
## User guide
|
## User guide
|
||||||
|
|
||||||
The centerpiece of this driver is the `detectorTowerAxis`, which controls the angle of the detector flight tube. Creating an instance of this axis type also creates multiple so-called `detectorTowerAuxiliaryAxis`, which are used to perform secondary movements.
|
The centerpiece of this driver is the `detectorTowerAngleAxis`, which controls the angle of the detector flight tube. Creating an instance of this axis type also creates multiple so-called `detectorTowerLiftAxis`, which are used to perform secondary movements.
|
||||||
|
|
||||||
The utilities provided in the `utils` folder of https://git.psi.ch/sinq-epics-modules/turboPmac work with this driver as well.
|
The utilities provided in the `utils` folder of https://git.psi.ch/sinq-epics-modules/turboPmac work with this driver as well.
|
||||||
|
|
||||||
@ -22,12 +22,12 @@ TODO: How to start combined movement
|
|||||||
|
|
||||||
detectorTower exports the following IOC shell functions:
|
detectorTower exports the following IOC shell functions:
|
||||||
- `detectorTowerController`: Create a new controller object.
|
- `detectorTowerController`: Create a new controller object.
|
||||||
- `detectorTowerAxis`: Create a new axis object.
|
- `detectorTowerAngleAxis`: Create a new axis object.
|
||||||
|
|
||||||
The constructor function for `detectorTowerAxis` has the following syntax:
|
The constructor function for `detectorTowerAngleAxis` has the following syntax:
|
||||||
|
|
||||||
```
|
```
|
||||||
detectorTowerAxis("$(NAME)",1, 2, 3)
|
detectorTowerAngleAxis("$(NAME)",1, 2, 3)
|
||||||
```
|
```
|
||||||
|
|
||||||
with 1 being the axis number / index of the detector flight tube angle axis, 2 being the lift offset axis and 3 being the tilt offset axis. These axes are parametrized in the same way as any "normal" axes via a substitution file (see corresponding section below).
|
with 1 being the axis number / index of the detector flight tube angle axis, 2 being the lift offset axis and 3 being the tilt offset axis. These axes are parametrized in the same way as any "normal" axes via a substitution file (see corresponding section below).
|
||||||
@ -51,7 +51,7 @@ drvAsynIPPortConfigure("$(ASYN_PORT)","172.28.101.24:1025")
|
|||||||
detectorTowerController("$(NAME)", "$(ASYN_PORT)", 8, 0.05, 1, 1);
|
detectorTowerController("$(NAME)", "$(ASYN_PORT)", 8, 0.05, 1, 1);
|
||||||
|
|
||||||
# Slot 1, 2 and 3 are occupied by a detector tower axis and its attached auxiliary axes, while the slots 4 and 5 are "normal" Turbo PMAC axes.
|
# Slot 1, 2 and 3 are occupied by a detector tower axis and its attached auxiliary axes, while the slots 4 and 5 are "normal" Turbo PMAC axes.
|
||||||
detectorTowerAxis("$(NAME)",1, 2, 3);
|
detectorTowerAngleAxis("$(NAME)",1, 2, 3);
|
||||||
turboPmacAxis("$(NAME)",4);
|
turboPmacAxis("$(NAME)",4);
|
||||||
turboPmacAxis("$(NAME)",5);
|
turboPmacAxis("$(NAME)",5);
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ dbLoadRecords("$(sinqMotor_DB)/asynRecord.db","P=$(INSTR)$(NAME),PORT=$(ASYN_POR
|
|||||||
|
|
||||||
From the perspective of EPICS, the main detector flight tube axis and the auxiliary axes are independent axes and therefore each axis needs its own parametrization in the substitution file. If additional axes are used in the axis, they are parametrized as usual:
|
From the perspective of EPICS, the main detector flight tube axis and the auxiliary axes are independent axes and therefore each axis needs its own parametrization in the substitution file. If additional axes are used in the axis, they are parametrized as usual:
|
||||||
```
|
```
|
||||||
detectorTowerAxis("$(NAME)",1, 2, 3);
|
detectorTowerAngleAxis("$(NAME)",1, 2, 3);
|
||||||
turboPmacAxis("$(NAME)",4);
|
turboPmacAxis("$(NAME)",4);
|
||||||
turboPmacAxis("$(NAME)",5);
|
turboPmacAxis("$(NAME)",5);
|
||||||
```
|
```
|
||||||
@ -99,13 +99,14 @@ Note that the speed of the detector tower axes 1, 2 and 3 cannot be set. Setting
|
|||||||
|
|
||||||
### Code architecture
|
### Code architecture
|
||||||
|
|
||||||
The code is designed around the `detectorTowerAxis`, which controls the angle of the beam guide and acts as a "master" axis. Other movements are realized as auxiliary axes (e.g. for the vertical lift offset and the tilt offset). The `detectorTowerAxis` has pointers to all associated auxiliary axes and (as described above) its IOC shell constructor also builds the associated auxiliary axes.
|
The code is designed around the `detectorTowerAngleAxis`, which controls the angle of the beam guide and acts as a "master" axis. Other movements are realized as auxiliary axes (e.g. for the vertical lift offset and the tilt offset). The `detectorTowerAngleAxis` has pointers to all associated auxiliary axes and (as described above) its IOC shell constructor also builds the associated auxiliary axes.
|
||||||
|
|
||||||
The `doPoll` implementation for `detectorTowerAxis` queries the status of both the `detectorTowerAxis` itself and all associated auxiliary axes. If any of the axes is moving, the `detectorTowerAxis` is set to "moving" as well. In turn, the `doPoll` implementation of a `detectorTowerAuxiliaryAxis` checks if its associated `detectorTowerAxis` is moving and sets its own movement status accordingly.
|
TODO
|
||||||
|
The `doPoll` implementation for `detectorTowerAngleAxis` queries the status of both the `detectorTowerAngleAxis` itself and all associated auxiliary axes. If any of the axes is moving, the `detectorTowerAngleAxis` is set to "moving" as well. In turn, the `doPoll` implementation of a `detectorTowerLiftAxis` checks if its associated `detectorTowerAngleAxis` is moving and sets its own movement status accordingly.
|
||||||
|
|
||||||
The `detectorTowerController` is a thin wrapper around a `turboPmacController` which overwrites the `readInt32` and `writeInt32` in order to support the PVs "$(INSTR)$(M):ChangeState", "$(INSTR)$(M):PositionStateRBV" and "$(INSTR)$(M):ChangingStateRBV". Any calls to these two methods not concerning the aforementioned PVs are directly forwarded to `turboPmacController::readInt32` / `turboPmacController::writeInt32`.
|
The `detectorTowerController` is a thin wrapper around a `turboPmacController` which overwrites the `readInt32` and `writeInt32` in order to support the PVs "$(INSTR)$(M):ChangeState", "$(INSTR)$(M):PositionStateRBV" and "$(INSTR)$(M):ChangingStateRBV". Any calls to these two methods not concerning the aforementioned PVs are directly forwarded to `turboPmacController::readInt32` / `turboPmacController::writeInt32`.
|
||||||
|
|
||||||
In order to save on movement time, movement commands to auxiliary axes and the `detectorTowerAxis` are collected and then send as a single resulting movement command to the MCU. In order to do so, a "collector" thread is running which checks if a movement request has been send to one of the axes. If that is the case, it waits for some time and checks if commands for other axes are given as well. Then, it calls `detectorTowerAxis::startCombinedMove` which combines all commands to a single request which is sent to the MCU. This allows the user to start a combined movement e.g. via caput:
|
In order to save on movement time, movement commands to auxiliary axes and the `detectorTowerAngleAxis` are collected and then send as a single resulting movement command to the MCU. In order to do so, a "collector" thread is running which checks if a movement request has been send to one of the axes. If that is the case, it waits for some time and checks if commands for other axes are given as well. Then, it calls `detectorTowerAngleAxis::startCombinedMove` which combines all commands to a single request which is sent to the MCU. This allows the user to start a combined movement e.g. via caput:
|
||||||
|
|
||||||
```
|
```
|
||||||
caput tower 2 & caput liftZeroCorr 10
|
caput tower 2 & caput liftZeroCorr 10
|
||||||
@ -123,4 +124,4 @@ Please see the documentation for the module sinqMotor: https://git.psi.ch/sinq-e
|
|||||||
|
|
||||||
### How to build it
|
### How to build it
|
||||||
|
|
||||||
Please see the documentation for the module sinqMotor: https://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/main/README.md.
|
This driver can be compiled and installed by running `make install` from the same directory where the Makefile is located. However, since it uses the git submodule turboPmac, make sure that the correct version of the submodule repository is checked out AND the change is commited (`git status` shows no non-committed changes). Please see the section "Usage as static dependency" in https://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/main/README.md for more details.
|
@ -1,12 +1,3 @@
|
|||||||
# Set to 0 to move the tower into working state and to 1 to move into changer position
|
|
||||||
record(longout, "$(INSTR)$(M):ChangeState") {
|
|
||||||
field(DTYP, "asynInt32")
|
|
||||||
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) CHANGE_STATE")
|
|
||||||
field(PINI, "NO")
|
|
||||||
field(DRVH, "1")
|
|
||||||
field(DRVL, "0")
|
|
||||||
}
|
|
||||||
|
|
||||||
# Read the position state of the axis:
|
# Read the position state of the axis:
|
||||||
# 0 = not ready
|
# 0 = not ready
|
||||||
# 1 = ready (in working position)
|
# 1 = ready (in working position)
|
||||||
@ -20,28 +11,163 @@ record(longin, "$(INSTR)$(M):PositionStateRBV")
|
|||||||
field(PINI, "NO")
|
field(PINI, "NO")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Set to 0 to move the tower into working state and to 1 to move into changer position.
|
||||||
|
# The PV "$(INSTR)$(M):ChangeStateRBV" can be used to check if starting the corresponding
|
||||||
|
# movement was successfull.
|
||||||
|
record(longout, "$(INSTR)$(M):ChangeState") {
|
||||||
|
field(DTYP, "asynInt32")
|
||||||
|
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) CHANGE_STATE")
|
||||||
|
field(PINI, "NO")
|
||||||
|
field(DRVH, "1")
|
||||||
|
field(DRVL, "0")
|
||||||
|
}
|
||||||
|
|
||||||
|
# When a value is written to the PV "$(INSTR)$(M):ChangeState", the tower needs to be
|
||||||
|
# idle (PositionState = 0), otherwise the state will not be changed. If the value
|
||||||
|
# of this PV is equal to "$(INSTR)$(M):ChangeState", starting a state change was
|
||||||
|
# successfull, otherwise not.
|
||||||
|
record(longin, "$(INSTR)$(M):ChangeStateRBV")
|
||||||
|
{
|
||||||
|
field(DTYP, "asynInt32")
|
||||||
|
field(INP, "@asyn($(CONTROLLER),$(AXIS)) CHANGE_STATE_RBV")
|
||||||
|
field(SCAN, "I/O Intr")
|
||||||
|
field(PINI, "NO")
|
||||||
|
}
|
||||||
|
|
||||||
# This PV combines the position state and the movement readback value from the
|
# This PV combines the position state and the movement readback value from the
|
||||||
# motor record:
|
# motor record:
|
||||||
# - 0, if the tower is in working state or transitioning to change position
|
# - 0, if the tower is in working state or transitioning to change position
|
||||||
# - 1, If the tower is in change position or transitioning to working state
|
# - 1, If the tower is in change position or transitioning to working state
|
||||||
record(calc, "$(INSTR)$(M):ChangingStateRBV")
|
record(calc, "$(INSTR)$(M):ChangingStateRBV")
|
||||||
{
|
{
|
||||||
field(INPA, "$(INSTR)$(M).MOVN CP")
|
field(INPA, "$(INSTR)$(M):PositionStateRBV CP")
|
||||||
field(INPB, "$(INSTR)$(M):PositionStateRBV CP")
|
field(INPB, "$(INSTR)$(M).MOVN CP")
|
||||||
field(CALC, "(B == 1 || (B == 2 && A == 1)) ? 0 : 1")
|
field(CALC, "((A == 2 && B == 0) || A == 3) ? 1 : 0")
|
||||||
}
|
}
|
||||||
|
|
||||||
# Convert the double value from the calc record to an integer value
|
# Convert the double value from the calc record to an integer value.
|
||||||
record(longout, "$(INSTR)$(M):ChangingStateRBV_int") {
|
record(longout, "$(INSTR)$(M):ChangingStateRBV_int") {
|
||||||
field(DOL, "$(INSTR)$(M):ChangingStateRBV CP")
|
field(DOL, "$(INSTR)$(M):ChangingStateRBV CP")
|
||||||
field(OMSL, "closed_loop")
|
field(OMSL, "closed_loop")
|
||||||
}
|
}
|
||||||
|
|
||||||
# Set an additional offset to the target position of an auxiliary axis
|
# Read out the origin of the corresponding axis by the given value.
|
||||||
# For all other axis types, writing to this parameter does nothing.
|
# This PV does nothing for "normal" Turbo PMAC axes.
|
||||||
record(ao, "$(INSTR)$(M):TargetOffset") {
|
record(ai, "$(INSTR)$(M):Origin") {
|
||||||
field(DTYP, "asynFloat64")
|
field(DTYP, "asynFloat64")
|
||||||
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) MOTOR_TARGET_OFFSET")
|
field(INP, "@asyn($(CONTROLLER),$(AXIS)) MOTOR_ORIGIN")
|
||||||
field(PINI, "YES")
|
field(SCAN, "I/O Intr")
|
||||||
|
field(PINI, "NO")
|
||||||
field(VAL, "0")
|
field(VAL, "0")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Shift the origin of the corresponding axis by the given value. The axis will move to this
|
||||||
|
# position and the hardware controller will internally set this position as the
|
||||||
|
# new "0" value. This PV does nothing for "normal" Turbo PMAC axes.
|
||||||
|
record(ao, "$(INSTR)$(M):AdjustOrigin") {
|
||||||
|
field(DTYP, "Raw Soft Channel")
|
||||||
|
field(PINI, "NO")
|
||||||
|
field(FLNK, "$(INSTR)$(M):ResetAO")
|
||||||
|
field(VAL, "0")
|
||||||
|
}
|
||||||
|
|
||||||
|
# Reset the PV $(INSTR)$(M):AdjustOrigin to zero after it has been written to and
|
||||||
|
# forward the value to $(INSTR)$(M):WriteAO which does the actual writing.
|
||||||
|
record(seq, "$(INSTR)$(M):ResetAO") {
|
||||||
|
field(DESC, "Write value to hardware then reset to 0")
|
||||||
|
field(DOL1, "$(INSTR)$(M):AdjustOrigin")
|
||||||
|
field(LNK1, "$(INSTR)$(M):WriteAO.VAL PP") # Perform write to hardware
|
||||||
|
field(DOL2, "0.0")
|
||||||
|
field(LNK2, "$(INSTR)$(M):AdjustOrigin.VAL PP") # Reset to zero
|
||||||
|
}
|
||||||
|
|
||||||
|
# This record forwards the adjustment of the origin to the asyn driver.
|
||||||
|
record(ao, "$(INSTR)$(M):WriteAO") {
|
||||||
|
field(DTYP, "asynFloat64")
|
||||||
|
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) MOTOR_ADJUST_ORIGIN")
|
||||||
|
field(PINI, "NO")
|
||||||
|
field(VAL, "0")
|
||||||
|
}
|
||||||
|
|
||||||
|
# Read out the origin of the lift axis support motor (FTZ at AMOR).
|
||||||
|
# This PV does nothing for "normal" Turbo PMAC axes or the angle axis.
|
||||||
|
record(ai, "$(INSTR)$(M):SupportOrigin") {
|
||||||
|
field(DTYP, "asynFloat64")
|
||||||
|
field(INP, "@asyn($(CONTROLLER),$(AXIS)) SUPPORT_ORIGIN")
|
||||||
|
field(SCAN, "I/O Intr")
|
||||||
|
field(PINI, "NO")
|
||||||
|
field(VAL, "0")
|
||||||
|
}
|
||||||
|
|
||||||
|
# Shift the origin of the lift support axis by the given value. The axis will
|
||||||
|
# move to this position and the hardware controller will internally set this
|
||||||
|
# position as the new "0" value. This PV does nothing for "normal" Turbo PMAC
|
||||||
|
# axes or the angle axis.
|
||||||
|
record(ao, "$(INSTR)$(M):AdjustSupportOrigin") {
|
||||||
|
field(DTYP, "Raw Soft Channel")
|
||||||
|
field(PINI, "NO")
|
||||||
|
field(DRVH, "200") # Limit in encoder steps (not mm!)
|
||||||
|
field(DRVL, "-200") # Limit in encoder steps (not mm!)
|
||||||
|
field(FLNK, "$(INSTR)$(M):ResetASO")
|
||||||
|
field(VAL, "0")
|
||||||
|
}
|
||||||
|
|
||||||
|
# Reset the PV $(INSTR)$(M):AdjustOrigin to zero after it has been written to and
|
||||||
|
# forward the value to $(INSTR)$(M):WriteAO which does the actual writing.
|
||||||
|
record(seq, "$(INSTR)$(M):ResetASO") {
|
||||||
|
field(DESC, "Write value to hardware then reset to 0")
|
||||||
|
field(DOL1, "$(INSTR)$(M):AdjustSupportOrigin")
|
||||||
|
field(LNK1, "$(INSTR)$(M):WriteASO.VAL PP") # Perform write to hardware
|
||||||
|
field(DOL2, "0.0")
|
||||||
|
field(LNK2, "$(INSTR)$(M):AdjustSupportOrigin.VAL PP") # Reset to zero
|
||||||
|
}
|
||||||
|
|
||||||
|
# This record forwards the adjustment of the origin to the asyn driver.
|
||||||
|
record(ao, "$(INSTR)$(M):WriteASO") {
|
||||||
|
field(DTYP, "asynFloat64")
|
||||||
|
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) SUPPORT_ADJUST_ORIGIN")
|
||||||
|
field(PINI, "NO")
|
||||||
|
field(VAL, "0")
|
||||||
|
}
|
||||||
|
|
||||||
|
# This record pair reads the parameter library value for "AdjustOriginHighLimitFromDriver_"
|
||||||
|
# and pushes it to the high limit field of the "$(INSTR)$(M):AdjustOrigin" PV.
|
||||||
|
# This can be used to read limits from the hardware and correspondingly update the motor record from the driver.
|
||||||
|
# The implementation strategy is taken from https://epics.anl.gov/tech-talk/2022/msg00464.php.
|
||||||
|
# This record is coupled to the parameter library via AdjustOriginHighLimitFromDriver_ -> MOTOR_AOHL_FROM_DRIVER.
|
||||||
|
record(ai, "$(INSTR)$(M):AOHL_RBV")
|
||||||
|
{
|
||||||
|
field(DTYP, "asynFloat64")
|
||||||
|
field(INP, "@asyn($(CONTROLLER),$(AXIS)) MOTOR_AOHL_FROM_DRIVER")
|
||||||
|
field(SCAN, "I/O Intr")
|
||||||
|
field(FLNK, "$(INSTR)$(M):PushAOHL2Field")
|
||||||
|
field(PINI, "NO")
|
||||||
|
}
|
||||||
|
# Abbreviated name because EPICS apparently has a maximum record name length
|
||||||
|
record(ao, "$(INSTR)$(M):PushAOHL2Field") {
|
||||||
|
field(DOL, "$(INSTR)$(M):AOHL_RBV NPP")
|
||||||
|
field(OUT, "$(INSTR)$(M):AdjustOrigin.DRVH")
|
||||||
|
field(OMSL, "closed_loop")
|
||||||
|
field(PINI, "NO")
|
||||||
|
}
|
||||||
|
|
||||||
|
# This record pair reads the parameter library value for "motorLowLimitFromDriver_"
|
||||||
|
# and pushes it to the low limit field of the "$(INSTR)$(M):AdjustOrigin" PV.
|
||||||
|
# This can be used to read limits from the hardware and correspondingly update the motor record from the driver.
|
||||||
|
# The implementation strategy is taken from https://epics.anl.gov/tech-talk/2022/msg00464.php.
|
||||||
|
# This record is coupled to the parameter library via AdjustOriginLowLimitFromDriver_ -> MOTOR_AOLL_FROM_DRIVER.
|
||||||
|
record(ai, "$(INSTR)$(M):AOLL_RBV")
|
||||||
|
{
|
||||||
|
field(DTYP, "asynFloat64")
|
||||||
|
field(INP, "@asyn($(CONTROLLER),$(AXIS)) MOTOR_AOLL_FROM_DRIVER")
|
||||||
|
field(SCAN, "I/O Intr")
|
||||||
|
field(FLNK, "$(INSTR)$(M):PushAOLL2Field")
|
||||||
|
field(PINI, "NO")
|
||||||
|
}
|
||||||
|
# Abbreviated name because EPICS apparently has a maximum record name length
|
||||||
|
record(ao, "$(INSTR)$(M):PushAOLL2Field") {
|
||||||
|
field(DOL, "$(INSTR)$(M):AOLL_RBV NPP")
|
||||||
|
field(OUT, "$(INSTR)$(M):AdjustOrigin.DRVL")
|
||||||
|
field(OMSL, "closed_loop")
|
||||||
|
field(PINI, "NO")
|
||||||
|
}
|
||||||
|
693
src/detectorTowerAngleAxis.cpp
Normal file
693
src/detectorTowerAngleAxis.cpp
Normal file
@ -0,0 +1,693 @@
|
|||||||
|
#include "detectorTowerAngleAxis.h"
|
||||||
|
#include "detectorTowerController.h"
|
||||||
|
#include "turboPmacController.h"
|
||||||
|
#include <epicsExport.h>
|
||||||
|
#include <errlog.h>
|
||||||
|
#include <iocsh.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
Contains all instances of detectorTowerAngleAxis which have been created and is
|
||||||
|
used in the initialization hook function.
|
||||||
|
*/
|
||||||
|
static std::vector<detectorTowerAngleAxis *> axes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Hook function to perform certain actions during the IOC initialization
|
||||||
|
*
|
||||||
|
* @param iState
|
||||||
|
*/
|
||||||
|
static void epicsInithookFunction(initHookState iState) {
|
||||||
|
if (iState == initHookAfterDatabaseRunning) {
|
||||||
|
// Iterate through all axes of each and call the initialization method
|
||||||
|
// on each one of them.
|
||||||
|
for (std::vector<detectorTowerAngleAxis *>::iterator itA = axes.begin();
|
||||||
|
itA != axes.end(); ++itA) {
|
||||||
|
detectorTowerAngleAxis *axis = *itA;
|
||||||
|
axis->init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void deferredMovementCollectorLoop(void *drvPvt) {
|
||||||
|
detectorTowerAngleAxis *axis = (detectorTowerAngleAxis *)drvPvt;
|
||||||
|
while (1) {
|
||||||
|
if (axis->receivedTarget_) {
|
||||||
|
// Wait for 100 ms and then start the movement with the information
|
||||||
|
// available
|
||||||
|
axis->startingDeferredMovement_ = true;
|
||||||
|
epicsThreadSleep(axis->deferredMovementWait_);
|
||||||
|
axis->startCombinedMove();
|
||||||
|
|
||||||
|
// After the movement command has been send, reset the flag
|
||||||
|
axis->receivedTarget_ = false;
|
||||||
|
}
|
||||||
|
// Limit this loop to an idle frequency of 1 kHz
|
||||||
|
epicsThreadSleep(0.001);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
detectorTowerAngleAxis::detectorTowerAngleAxis(detectorTowerController *pC,
|
||||||
|
int axisNo,
|
||||||
|
detectorTowerLiftAxis *liftAxis)
|
||||||
|
: turboPmacAxis(pC, axisNo, false), pC_(pC) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
The superclass constructor sinqAxis calls in turn its superclass constructor
|
||||||
|
asynMotorAxis. In the latter, a pointer to the constructed object this is
|
||||||
|
stored inside the array pAxes_:
|
||||||
|
|
||||||
|
pC->pAxes_[axisNo] = this;
|
||||||
|
|
||||||
|
Therefore, the axes are managed by the controller pC. If axisNo is out of
|
||||||
|
bounds, asynMotorAxis prints an error (see
|
||||||
|
https://github.com/epics-modules/motor/blob/master/motorApp/MotorSrc/asynMotorAxis.cpp,
|
||||||
|
line 40). However, we want the IOC creation to stop completely, since this
|
||||||
|
is a configuration error.
|
||||||
|
*/
|
||||||
|
if (axisNo >= pC->numAxes()) {
|
||||||
|
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
|
"Controller \"%s\", axis %d => %s, line %d: FATAL ERROR: "
|
||||||
|
"Axis index %d must be smaller than the total number of axes "
|
||||||
|
"%d",
|
||||||
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||||
|
axisNo_, pC->numAxes());
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize all member variables
|
||||||
|
// Assumed to be not ready by default, this is overwritten in the next poll
|
||||||
|
error_ = 0;
|
||||||
|
receivedTarget_ = false;
|
||||||
|
startingDeferredMovement_ = false;
|
||||||
|
deferredMovementWait_ = 0.1; // seconds
|
||||||
|
liftAxis_ = liftAxis;
|
||||||
|
liftAxis_->angleAxis_ = this;
|
||||||
|
|
||||||
|
// Will be populated in the init() method
|
||||||
|
beamRadius_ = 0.0;
|
||||||
|
|
||||||
|
// Register the hook function during construction of the first axis object
|
||||||
|
if (axes.empty()) {
|
||||||
|
initHookRegister(&epicsInithookFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect all axes into this list which will be used in the hook
|
||||||
|
// function
|
||||||
|
axes.push_back(this);
|
||||||
|
|
||||||
|
// Create a thread which collects all movement commands send to components
|
||||||
|
// of the virtual axis. After a component received a new target position,
|
||||||
|
// it forwards this information to the thread. The thread then waits a short
|
||||||
|
// time to see if other components also received new targets and starts the
|
||||||
|
// movement with all targets afterwards.
|
||||||
|
epicsThreadCreate("deferredMovement", epicsThreadPriorityLow,
|
||||||
|
epicsThreadGetStackSize(epicsThreadStackMedium),
|
||||||
|
(EPICSTHREADFUNC)deferredMovementCollectorLoop,
|
||||||
|
(void *)this);
|
||||||
|
}
|
||||||
|
|
||||||
|
detectorTowerAngleAxis::~detectorTowerAngleAxis(void) {
|
||||||
|
// Since the controller memory is managed somewhere else, we don't need to
|
||||||
|
// clean up the pointer pC here.
|
||||||
|
}
|
||||||
|
|
||||||
|
asynStatus detectorTowerAngleAxis::init() {
|
||||||
|
|
||||||
|
// Local variable declaration
|
||||||
|
asynStatus status = asynSuccess;
|
||||||
|
double motorRecResolution = 0.0;
|
||||||
|
char response[pC_->MAXBUF_] = {0};
|
||||||
|
int nvals = 0;
|
||||||
|
|
||||||
|
// The parameter library takes some time to be initialized. Therefore we
|
||||||
|
// wait until the status is not asynParamUndefined anymore.
|
||||||
|
time_t now = time(NULL);
|
||||||
|
time_t maxInitTime = 60;
|
||||||
|
while (1) {
|
||||||
|
status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution(),
|
||||||
|
&motorRecResolution);
|
||||||
|
if (status == asynParamUndefined) {
|
||||||
|
if (now + maxInitTime < time(NULL)) {
|
||||||
|
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
|
"Controller \"%s\", axis "
|
||||||
|
"%ddeferredMovementCollectorLoop => %s, line "
|
||||||
|
"%d\nInitializing the parameter library failed.\n",
|
||||||
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__,
|
||||||
|
__LINE__);
|
||||||
|
return asynError;
|
||||||
|
}
|
||||||
|
} else if (status == asynSuccess) {
|
||||||
|
break;
|
||||||
|
} else if (status != asynSuccess) {
|
||||||
|
return pC_->paramLibAccessFailed(status, "motorRecResolution_",
|
||||||
|
axisNo_, __PRETTY_FUNCTION__,
|
||||||
|
__LINE__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the detector radius
|
||||||
|
const char *command = "P459";
|
||||||
|
status = pC_->writeRead(axisNo_, command, response, 1);
|
||||||
|
if (status != asynSuccess) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
nvals = sscanf(response, "%lf", &beamRadius_);
|
||||||
|
if (nvals != 1) {
|
||||||
|
return pC_->couldNotParseResponse(command, response, axisNo_,
|
||||||
|
__PRETTY_FUNCTION__, __LINE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the motorStatusMoving flag
|
||||||
|
status = setIntegerParam(pC_->motorStatusMoving(), 0);
|
||||||
|
if (status != asynSuccess) {
|
||||||
|
return pC_->paramLibAccessFailed(status, "motorStatusMoving_", axisNo_,
|
||||||
|
__PRETTY_FUNCTION__, __LINE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform the actual poll
|
||||||
|
asynStatus detectorTowerAngleAxis::poll(bool *moving) {
|
||||||
|
asynStatus status = asynSuccess;
|
||||||
|
|
||||||
|
// Is this axis the one with the smallest index?
|
||||||
|
// If not, just read out the movement status and update *moving
|
||||||
|
if (axisNo() < liftAxis_->axisNo()) {
|
||||||
|
status = pC_->pollDetectorAxes(moving, this, liftAxis_);
|
||||||
|
} else {
|
||||||
|
status = pC_->getIntegerParam(axisNo(), pC_->motorStatusMoving(),
|
||||||
|
(int *)moving);
|
||||||
|
if (status != asynSuccess) {
|
||||||
|
return pC_->paramLibAccessFailed(status, "motorStatusMoving",
|
||||||
|
axisNo_, __PRETTY_FUNCTION__,
|
||||||
|
__LINE__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wasMoving_ = *moving;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
asynStatus detectorTowerAngleAxis::doPoll(bool *moving) {
|
||||||
|
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
|
"Controller \"%s\", axis %d => %s, line %d\nThe doPoll method "
|
||||||
|
"of this axis type should not be reachable. This is a bug.\n",
|
||||||
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
||||||
|
return asynError;
|
||||||
|
}
|
||||||
|
|
||||||
|
asynStatus detectorTowerAngleAxis::doMove(double position, int relative,
|
||||||
|
double min_velocity,
|
||||||
|
double max_velocity,
|
||||||
|
double acceleration) {
|
||||||
|
double motorRecResolution = 0.0;
|
||||||
|
asynStatus plStatus = pC_->getDoubleParam(
|
||||||
|
axisNo_, pC_->motorRecResolution(), &motorRecResolution);
|
||||||
|
if (plStatus != asynSuccess) {
|
||||||
|
return pC_->paramLibAccessFailed(plStatus, "motorRecResolution_",
|
||||||
|
axisNo_, __PRETTY_FUNCTION__,
|
||||||
|
__LINE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signal to the deferredMovementCollectorLoop that a movement should be
|
||||||
|
// started to the defined target position.
|
||||||
|
targetPosition_ = position * motorRecResolution;
|
||||||
|
receivedTarget_ = true;
|
||||||
|
|
||||||
|
return asynSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
asynStatus detectorTowerAngleAxis::startCombinedMove() {
|
||||||
|
|
||||||
|
// Status of read-write-operations of ASCII commands to the controller
|
||||||
|
asynStatus rwStatus = asynSuccess;
|
||||||
|
|
||||||
|
// Status of parameter library operations
|
||||||
|
asynStatus plStatus = asynSuccess;
|
||||||
|
|
||||||
|
char command[pC_->MAXBUF_] = {0};
|
||||||
|
char response[pC_->MAXBUF_] = {0};
|
||||||
|
double motorCoordinatesPosition = 0.0;
|
||||||
|
int positionState = 0;
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
plStatus =
|
||||||
|
pC_->getIntegerParam(axisNo_, pC_->positionStateRBV(), &positionState);
|
||||||
|
if (plStatus != asynSuccess) {
|
||||||
|
return pC_->paramLibAccessFailed(plStatus, "positionStateRBV_", axisNo_,
|
||||||
|
__PRETTY_FUNCTION__, __LINE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the axis is in changer position, it must be moved into working
|
||||||
|
// position before any move can be started.
|
||||||
|
bool isInChangerPos = positionState == 2 || positionState == 3;
|
||||||
|
if (pC_->getMsgPrintControl().shouldBePrinted(
|
||||||
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||||
|
isInChangerPos, pC_->pasynUser())) {
|
||||||
|
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
|
"Controller \"%s\", axis %d => %s, line %d\nAxis cannot be "
|
||||||
|
"moved because it is moving from working to changer "
|
||||||
|
"position, is in changer position or is moving from changer "
|
||||||
|
"to working position.%s\n",
|
||||||
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||||
|
pC_->getMsgPrintControl().getSuffix());
|
||||||
|
}
|
||||||
|
if (isInChangerPos) {
|
||||||
|
startingDeferredMovement_ = false;
|
||||||
|
return asynError;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the target positions for beam tilt, detector tilt offset and lift
|
||||||
|
// offset
|
||||||
|
snprintf(command, sizeof(command), "Q451=%lf Q454=%lf P350=1",
|
||||||
|
targetPosition_, liftAxis_->targetPosition_);
|
||||||
|
|
||||||
|
// Lock the access to the controller since this function runs in another
|
||||||
|
// thread than the poll method.
|
||||||
|
pC_->lock();
|
||||||
|
|
||||||
|
// We don't expect an answer
|
||||||
|
rwStatus = pC_->writeRead(axisNo_, command, response, 0);
|
||||||
|
|
||||||
|
// Free the controller again
|
||||||
|
pC_->unlock();
|
||||||
|
|
||||||
|
if (rwStatus != asynSuccess) {
|
||||||
|
asynPrint(
|
||||||
|
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
|
"Controller \"%s\", axis %d => %s, line %d\nStarting movement to "
|
||||||
|
"target position %lf failed.\n",
|
||||||
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||||
|
motorCoordinatesPosition);
|
||||||
|
plStatus = setIntegerParam(pC_->motorStatusProblem(), true);
|
||||||
|
if (plStatus != asynSuccess) {
|
||||||
|
return pC_->paramLibAccessFailed(plStatus, "motorStatusProblem_",
|
||||||
|
axisNo_, __PRETTY_FUNCTION__,
|
||||||
|
__LINE__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rwStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
asynStatus detectorTowerAngleAxis::stop(double acceleration) {
|
||||||
|
|
||||||
|
// Status of read-write-operations of ASCII commands to the controller
|
||||||
|
asynStatus rwStatus = asynSuccess;
|
||||||
|
|
||||||
|
// Status of parameter library operations
|
||||||
|
asynStatus plStatus = asynSuccess;
|
||||||
|
|
||||||
|
char response[pC_->MAXBUF_] = {0};
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
rwStatus = pC_->writeRead(axisNo_, "P350=8", response, 0);
|
||||||
|
|
||||||
|
if (rwStatus != asynSuccess) {
|
||||||
|
asynPrint(
|
||||||
|
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
|
"Controller \"%s\", axis %d => %s, line %d\nStopping the movement "
|
||||||
|
"failed.\n",
|
||||||
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
||||||
|
|
||||||
|
plStatus = setIntegerParam(pC_->motorStatusProblem(), true);
|
||||||
|
if (plStatus != asynSuccess) {
|
||||||
|
return pC_->paramLibAccessFailed(plStatus, "motorStatusProblem_",
|
||||||
|
axisNo_, __PRETTY_FUNCTION__,
|
||||||
|
__LINE__);
|
||||||
|
}
|
||||||
|
return asynError;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rwStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The detector tower axis uses absolute encoders
|
||||||
|
asynStatus detectorTowerAngleAxis::readEncoderType() {
|
||||||
|
asynStatus status = setStringParam(pC_->encoderType(), AbsoluteEncoder);
|
||||||
|
if (status != asynSuccess) {
|
||||||
|
return pC_->paramLibAccessFailed(status, "encoderType_", axisNo_,
|
||||||
|
__PRETTY_FUNCTION__, __LINE__);
|
||||||
|
}
|
||||||
|
return asynSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
asynStatus
|
||||||
|
detectorTowerAngleAxis::toggleWorkingChangerState(bool toChangingPosition) {
|
||||||
|
|
||||||
|
char response[pC_->MAXBUF_] = {0};
|
||||||
|
|
||||||
|
// Status of read-write-operations of ASCII commands to the controller
|
||||||
|
asynStatus rwStatus = asynSuccess;
|
||||||
|
|
||||||
|
// Status of parameter library operations
|
||||||
|
asynStatus plStatus = asynSuccess;
|
||||||
|
|
||||||
|
bool moving = false;
|
||||||
|
int positionState = 0;
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
rwStatus = poll(&moving);
|
||||||
|
if (rwStatus != asynSuccess) {
|
||||||
|
return rwStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
plStatus =
|
||||||
|
pC_->getIntegerParam(axisNo_, pC_->positionStateRBV(), &positionState);
|
||||||
|
if (plStatus != asynSuccess) {
|
||||||
|
return pC_->paramLibAccessFailed(plStatus, "positionStateRBV_", axisNo_,
|
||||||
|
__PRETTY_FUNCTION__, __LINE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (moving) {
|
||||||
|
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
|
"Controller \"%s\", axis %d => %s, line %d\nAxis is not "
|
||||||
|
"idle and can therefore not be moved to %s state.%s\n",
|
||||||
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||||
|
toChangingPosition ? "changer" : "working",
|
||||||
|
pC_->getMsgPrintControl().getSuffix());
|
||||||
|
|
||||||
|
plStatus = setStringParam(
|
||||||
|
pC_->motorMessageText(),
|
||||||
|
"Axis cannot be moved to changer position while it is moving.");
|
||||||
|
if (plStatus != asynSuccess) {
|
||||||
|
return pC_->paramLibAccessFailed(plStatus, "motorMessageText_",
|
||||||
|
axisNo_, __PRETTY_FUNCTION__,
|
||||||
|
__LINE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
plStatus = setIntegerParam(pC_->changeStateRBV(), !toChangingPosition);
|
||||||
|
if (plStatus != asynSuccess) {
|
||||||
|
return pC_->paramLibAccessFailed(plStatus, "changeStateRBV",
|
||||||
|
axisNo_, __PRETTY_FUNCTION__,
|
||||||
|
__LINE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
return asynError;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Axis is already in the correct position
|
||||||
|
bool isAlreadyThere = (toChangingPosition == false && positionState == 1) ||
|
||||||
|
(toChangingPosition == true && positionState == 2);
|
||||||
|
|
||||||
|
if (isAlreadyThere) {
|
||||||
|
asynPrint(pC_->pasynUser(), ASYN_TRACE_FLOW,
|
||||||
|
"Controller \"%s\", axis %d => %s, line %d\nAxis is already "
|
||||||
|
"in %s position.%s\n",
|
||||||
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||||
|
toChangingPosition ? "changer" : "working",
|
||||||
|
pC_->getMsgPrintControl().getSuffix());
|
||||||
|
|
||||||
|
// Update the PV anyway, even though nothing changed.
|
||||||
|
plStatus = setIntegerParam(pC_->changeStateRBV(), toChangingPosition);
|
||||||
|
if (plStatus != asynSuccess) {
|
||||||
|
return pC_->paramLibAccessFailed(plStatus, "changeStateRBV",
|
||||||
|
axisNo_, __PRETTY_FUNCTION__,
|
||||||
|
__LINE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
return asynSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move the axis into changer or working position
|
||||||
|
if (toChangingPosition) {
|
||||||
|
rwStatus = pC_->writeRead(axisNo_, "P350=2", response, 0);
|
||||||
|
plStatus = setStringParam(pC_->motorMessageText(),
|
||||||
|
"Moving to changer position ...");
|
||||||
|
} else {
|
||||||
|
rwStatus = pC_->writeRead(axisNo_, "P350=3", response, 0);
|
||||||
|
plStatus = setStringParam(pC_->motorMessageText(),
|
||||||
|
"Moving to working state ...");
|
||||||
|
}
|
||||||
|
if (plStatus != asynSuccess) {
|
||||||
|
plStatus = setIntegerParam(pC_->changeStateRBV(), !toChangingPosition);
|
||||||
|
if (plStatus != asynSuccess) {
|
||||||
|
return pC_->paramLibAccessFailed(plStatus, "changeStateRBV",
|
||||||
|
axisNo_, __PRETTY_FUNCTION__,
|
||||||
|
__LINE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pC_->paramLibAccessFailed(plStatus, "motorMessageText_", axisNo_,
|
||||||
|
__PRETTY_FUNCTION__, __LINE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
plStatus = setIntegerParam(pC_->changeStateRBV(), toChangingPosition);
|
||||||
|
if (plStatus != asynSuccess) {
|
||||||
|
return pC_->paramLibAccessFailed(plStatus, "changeStateRBV", axisNo_,
|
||||||
|
__PRETTY_FUNCTION__, __LINE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rwStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
asynStatus detectorTowerAngleAxis::doReset() {
|
||||||
|
|
||||||
|
char response[pC_->MAXBUF_] = {0};
|
||||||
|
asynStatus plStatus = asynSuccess;
|
||||||
|
int positionState = 0;
|
||||||
|
|
||||||
|
plStatus =
|
||||||
|
pC_->getIntegerParam(axisNo_, pC_->positionStateRBV(), &positionState);
|
||||||
|
if (plStatus != asynSuccess) {
|
||||||
|
return pC_->paramLibAccessFailed(plStatus, "positionStateRBV_", axisNo_,
|
||||||
|
__PRETTY_FUNCTION__, __LINE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Check which action should be performed:
|
||||||
|
- If error_ == 10 or 11 (FTZ motor error): P352 = 3 (Recover FTZ)
|
||||||
|
- If any other error: P352 = 2 (Reset error)
|
||||||
|
- Otherwise: P352 = 1 (Set axis in closed-loop mode)
|
||||||
|
*/
|
||||||
|
if (error_ == 10 || error_ == 11) {
|
||||||
|
return pC_->writeRead(axisNo_, "P352=3", response, 0);
|
||||||
|
} else if (error_ != 0) {
|
||||||
|
return pC_->writeRead(axisNo_, "P352=2", response, 0);
|
||||||
|
} else {
|
||||||
|
return pC_->writeRead(axisNo_, "P352=1", response, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
asynStatus detectorTowerAngleAxis::adjustOrigin(double newOrigin) {
|
||||||
|
|
||||||
|
asynStatus status = asynSuccess;
|
||||||
|
char command[pC_->MAXBUF_] = {0};
|
||||||
|
char response[pC_->MAXBUF_] = {0};
|
||||||
|
int positionState = 0;
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
status =
|
||||||
|
pC_->getIntegerParam(axisNo_, pC_->positionStateRBV(), &positionState);
|
||||||
|
if (status != asynSuccess) {
|
||||||
|
return pC_->paramLibAccessFailed(status, "positionStateRBV_", axisNo_,
|
||||||
|
__PRETTY_FUNCTION__, __LINE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the axis is in changer position, it must be moved into working
|
||||||
|
// position before any move can be started.
|
||||||
|
bool isInChangerPos = positionState == 2 || positionState == 3;
|
||||||
|
if (pC_->getMsgPrintControl().shouldBePrinted(
|
||||||
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||||
|
isInChangerPos, pC_->pasynUser())) {
|
||||||
|
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
|
"Controller \"%s\", axis %d => %s, line %d\nAxis cannot be "
|
||||||
|
"moved because it is moving from working to changer "
|
||||||
|
"position, is in changer position or is moving from changer "
|
||||||
|
"to working position.%s\n",
|
||||||
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||||
|
pC_->getMsgPrintControl().getSuffix());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the new origin for the angle axis
|
||||||
|
snprintf(command, sizeof(command), "Q556=%lf P350=4", newOrigin);
|
||||||
|
|
||||||
|
// We don't expect an answer
|
||||||
|
status = pC_->writeRead(axisNo_, command, response, 0);
|
||||||
|
|
||||||
|
if (status != asynSuccess) {
|
||||||
|
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
|
"Controller \"%s\", axis %d => %s, line %d\nSetting new "
|
||||||
|
"angle origin %lf failed.\n",
|
||||||
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||||
|
newOrigin);
|
||||||
|
status = setIntegerParam(pC_->motorStatusProblem(), true);
|
||||||
|
if (status != asynSuccess) {
|
||||||
|
return pC_->paramLibAccessFailed(status, "motorStatusProblem_",
|
||||||
|
axisNo_, __PRETTY_FUNCTION__,
|
||||||
|
__LINE__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************************/
|
||||||
|
/** The following functions are C-wrappers, and can be called directly from
|
||||||
|
* iocsh */
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
/*
|
||||||
|
C wrapper for the axis constructor. Please refer to the detectorTower
|
||||||
|
constructor documentation. The controller is read from the portName.
|
||||||
|
*/
|
||||||
|
asynStatus detectorTowerCreateAxis(const char *portName, int angleAxisIdx,
|
||||||
|
int liftAxisIdx) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
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 a lowLevelPortUser_ from a pointer to an asynPortDriver.
|
||||||
|
However, the given pointer is a nullptr and therefore doesn'taxis
|
||||||
|
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
|
||||||
|
detectorTowerController *pC = dynamic_cast<detectorTowerController *>(apd);
|
||||||
|
if (pC == nullptr) {
|
||||||
|
errlogPrintf("Controller \"%s\" => %s, line %d\nController "
|
||||||
|
"is not a detectorTowerController.",
|
||||||
|
portName, __PRETTY_FUNCTION__, __LINE__);
|
||||||
|
return asynError;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert that the three indices are different from each other
|
||||||
|
if (angleAxisIdx == liftAxisIdx) {
|
||||||
|
errlogPrintf("Controller \"%s\" => %s, line %d\nAll axis indices "
|
||||||
|
"must be unique.",
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
detectorTowerLiftAxis *liftAxis =
|
||||||
|
new detectorTowerLiftAxis(pC, liftAxisIdx);
|
||||||
|
|
||||||
|
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
||||||
|
#pragma GCC diagnostic ignored "-Wunused-variable"
|
||||||
|
detectorTowerAngleAxis *pAxis =
|
||||||
|
new detectorTowerAngleAxis(pC, angleAxisIdx, liftAxis);
|
||||||
|
|
||||||
|
// Allow manipulation of the controller again
|
||||||
|
pC->unlock();
|
||||||
|
return asynSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const iocshArg CreateAxisArg0 = {"Controller name (e.g. mcu1)",
|
||||||
|
iocshArgString};
|
||||||
|
static const iocshArg CreateAxisArg1 = {"Number of the detector angle axis",
|
||||||
|
iocshArgInt};
|
||||||
|
static const iocshArg CreateAxisArg2 = {"Number of the lift axis", iocshArgInt};
|
||||||
|
static const iocshArg *const CreateAxisArgs[] = {
|
||||||
|
&CreateAxisArg0,
|
||||||
|
&CreateAxisArg1,
|
||||||
|
&CreateAxisArg2,
|
||||||
|
};
|
||||||
|
static const iocshFuncDef configDetectorTowerCreateAxis = {"detectorTowerAxis",
|
||||||
|
3, CreateAxisArgs};
|
||||||
|
static void configDetectorTowerCreateAxisCallFunc(const iocshArgBuf *args) {
|
||||||
|
detectorTowerCreateAxis(args[0].sval, args[1].ival, args[2].ival);
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the setDeferredMovementWait (FFI implementation)
|
||||||
|
*
|
||||||
|
* @param portName Name of the controller
|
||||||
|
* @param axisNo Axis number
|
||||||
|
* @param scaleMovTimeout Scaling factor (in seconds)
|
||||||
|
* @return asynStatus
|
||||||
|
*/
|
||||||
|
asynStatus setDeferredMovementWait(const char *portName, int axisNo,
|
||||||
|
double deferredMovementWait) {
|
||||||
|
|
||||||
|
sinqController *pC;
|
||||||
|
pC = (sinqController *)findAsynPortDriver(portName);
|
||||||
|
if (pC == nullptr) {
|
||||||
|
errlogPrintf("Controller \"%s\" => %s, line %d:\nPort %s not found.",
|
||||||
|
portName, __PRETTY_FUNCTION__, __LINE__, portName);
|
||||||
|
return asynError;
|
||||||
|
}
|
||||||
|
|
||||||
|
asynMotorAxis *asynAxis = pC->getAxis(axisNo);
|
||||||
|
detectorTowerAngleAxis *axis =
|
||||||
|
dynamic_cast<detectorTowerAngleAxis *>(asynAxis);
|
||||||
|
if (axis == nullptr) {
|
||||||
|
errlogPrintf("Controller \"%s\" => %s, line %d:\nAxis %d does not "
|
||||||
|
"exist or is not an instance of detectorTowerAngleAxis.",
|
||||||
|
portName, __PRETTY_FUNCTION__, __LINE__, axisNo);
|
||||||
|
return asynError;
|
||||||
|
}
|
||||||
|
|
||||||
|
axis->deferredMovementWait_ = deferredMovementWait;
|
||||||
|
return asynSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const iocshArg setDeferredMovementWaitArg0 = {"Controller port name",
|
||||||
|
iocshArgString};
|
||||||
|
static const iocshArg setDeferredMovementWaitArg1 = {"Axis number",
|
||||||
|
iocshArgInt};
|
||||||
|
static const iocshArg setDeferredMovementWaitArg2 = {
|
||||||
|
"Minimum time a deferred movement will be delayed in order to collect "
|
||||||
|
"commands in seconds",
|
||||||
|
iocshArgDouble};
|
||||||
|
static const iocshArg *const setDeferredMovementWaitArgs[] = {
|
||||||
|
&setDeferredMovementWaitArg0, &setDeferredMovementWaitArg1,
|
||||||
|
&setDeferredMovementWaitArg2};
|
||||||
|
static const iocshFuncDef setDeferredMovementWaitDef = {
|
||||||
|
"setDeferredMovementWait", 3, setDeferredMovementWaitArgs};
|
||||||
|
|
||||||
|
static void setDeferredMovementWaitCallFunc(const iocshArgBuf *args) {
|
||||||
|
setDeferredMovementWait(args[0].sval, args[1].ival, args[2].dval);
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
// This function is made known to EPICS in detectorTower.dbd and is
|
||||||
|
// called by EPICS in order to register both functions in the IOC shell
|
||||||
|
static void detectorTowerAxisRegister(void) {
|
||||||
|
iocshRegister(&configDetectorTowerCreateAxis,
|
||||||
|
configDetectorTowerCreateAxisCallFunc);
|
||||||
|
iocshRegister(&setDeferredMovementWaitDef, setDeferredMovementWaitCallFunc);
|
||||||
|
}
|
||||||
|
epicsExportRegistrar(detectorTowerAxisRegister);
|
||||||
|
|
||||||
|
} // extern "C"
|
@ -1,6 +1,6 @@
|
|||||||
#ifndef detectorTowerAxis_H
|
#ifndef detectorTowerAngleAxis_H
|
||||||
#define detectorTowerAxis_H
|
#define detectorTowerAngleAxis_H
|
||||||
#include "detectorTowerAuxiliaryAxis.h"
|
#include "detectorTowerLiftAxis.h"
|
||||||
#include "turboPmacAxis.h"
|
#include "turboPmacAxis.h"
|
||||||
|
|
||||||
// Forward declaration of the controller class to resolve the cyclic dependency
|
// Forward declaration of the controller class to resolve the cyclic dependency
|
||||||
@ -8,10 +8,10 @@
|
|||||||
// https://en.cppreference.com/w/cpp/language/class.
|
// https://en.cppreference.com/w/cpp/language/class.
|
||||||
class detectorTowerController;
|
class detectorTowerController;
|
||||||
|
|
||||||
class detectorTowerAxis : public turboPmacAxis {
|
class detectorTowerAngleAxis : public turboPmacAxis {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Construct a new detectorTowerAxis
|
* @brief Construct a new detectorTowerAngleAxis
|
||||||
*
|
*
|
||||||
* @param pController Pointer to the associated controller
|
* @param pController Pointer to the associated controller
|
||||||
* @param axisNo Index of the axis
|
* @param axisNo Index of the axis
|
||||||
@ -20,15 +20,14 @@ class detectorTowerAxis : public turboPmacAxis {
|
|||||||
* @param tiltZeroCorrAxis Pointer to the attached axis which controls
|
* @param tiltZeroCorrAxis Pointer to the attached axis which controls
|
||||||
* the tilt offset
|
* the tilt offset
|
||||||
*/
|
*/
|
||||||
detectorTowerAxis(detectorTowerController *pController, int axisNo,
|
detectorTowerAngleAxis(detectorTowerController *pController, int axisNo,
|
||||||
detectorTowerAuxiliaryAxis *liftZeroCorrAxis,
|
detectorTowerLiftAxis *liftAxis);
|
||||||
detectorTowerAuxiliaryAxis *tiltZeroCorrAxis);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Destroy the detectorTowerAxis
|
* @brief Destroy the detectorTowerAngleAxis
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
virtual ~detectorTowerAxis();
|
virtual ~detectorTowerAngleAxis();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Readout of some values from the controller at IOC startup
|
* @brief Readout of some values from the controller at IOC startup
|
||||||
@ -47,12 +46,17 @@ class detectorTowerAxis : public turboPmacAxis {
|
|||||||
asynStatus stop(double acceleration);
|
asynStatus stop(double acceleration);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Implementation of the `doPoll` function from sinqAxis. The
|
* @brief Poll all detector tower axes, if this axis is the one with the
|
||||||
* parameters are described in the documentation of `sinqAxis::doPoll`.
|
* smallest index.
|
||||||
|
*
|
||||||
|
* We do not use the doPoll framework from sinqMotor here on purpose, since
|
||||||
|
* we want to e.g. reset the motorStatusProblem for all axes at once at the
|
||||||
|
* beginning of the poll.
|
||||||
*
|
*
|
||||||
* @param moving
|
* @param moving
|
||||||
* @return asynStatus
|
* @return asynStatus
|
||||||
*/
|
*/
|
||||||
|
asynStatus poll(bool *moving);
|
||||||
asynStatus doPoll(bool *moving);
|
asynStatus doPoll(bool *moving);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -100,7 +104,32 @@ class detectorTowerAxis : public turboPmacAxis {
|
|||||||
*/
|
*/
|
||||||
asynStatus doReset();
|
asynStatus doReset();
|
||||||
|
|
||||||
// If true, either this axis or one of the detectorTowerAuxiliaryAxis
|
/**
|
||||||
|
* @brief Move the axis to the position `newOrigin` and recalibrate
|
||||||
|
*
|
||||||
|
* When calling this function, the angle axis moves to `newOrigin` and the
|
||||||
|
* hardware sets this position as the new origin.
|
||||||
|
*
|
||||||
|
* @param origin
|
||||||
|
* @return asynStatus
|
||||||
|
*/
|
||||||
|
asynStatus adjustOrigin(double newOrigin);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read out the beam radius
|
||||||
|
*
|
||||||
|
* @return double
|
||||||
|
*/
|
||||||
|
double beamRadius() { return beamRadius_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return a pointer to the associated angle axis
|
||||||
|
*
|
||||||
|
* @return detectorTowerAngleAxis*
|
||||||
|
*/
|
||||||
|
detectorTowerLiftAxis *liftAxis() { return liftAxis_; }
|
||||||
|
|
||||||
|
// If true, either this axis or one of the detectorTowerLiftAxis
|
||||||
// attached to it received a movement command.
|
// attached to it received a movement command.
|
||||||
bool receivedTarget_;
|
bool receivedTarget_;
|
||||||
|
|
||||||
@ -115,15 +144,19 @@ class detectorTowerAxis : public turboPmacAxis {
|
|||||||
*/
|
*/
|
||||||
double deferredMovementWait_;
|
double deferredMovementWait_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Variable holding the axis error for later use
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int error_;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
detectorTowerController *pC_;
|
detectorTowerController *pC_;
|
||||||
int error_;
|
detectorTowerLiftAxis *liftAxis_;
|
||||||
detectorTowerAuxiliaryAxis *tiltZeroCorrAxis_;
|
|
||||||
detectorTowerAuxiliaryAxis *liftZeroCorrAxis_;
|
|
||||||
double beamRadius_;
|
double beamRadius_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class detectorTowerAuxiliaryAxis;
|
friend class detectorTowerLiftAxis;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -1,244 +0,0 @@
|
|||||||
#include "detectorTowerAuxiliaryAxis.h"
|
|
||||||
#include "detectorTowerAxis.h"
|
|
||||||
#include "detectorTowerController.h"
|
|
||||||
#include "turboPmacController.h"
|
|
||||||
#include <epicsExport.h>
|
|
||||||
#include <errlog.h>
|
|
||||||
#include <iocsh.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
Contains all instances of detectorTowerAuxiliaryAxis which have been created and
|
|
||||||
is used in the initialization hook function.
|
|
||||||
*/
|
|
||||||
static std::vector<detectorTowerAuxiliaryAxis *> axes;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Hook function to perform certain actions during the IOC initialization
|
|
||||||
*
|
|
||||||
* @param iState
|
|
||||||
*/
|
|
||||||
static void epicsInithookFunction(initHookState iState) {
|
|
||||||
if (iState == initHookAfterDatabaseRunning) {
|
|
||||||
// Iterate through all axes of each and call the initialization method
|
|
||||||
// on each one of them.
|
|
||||||
for (std::vector<detectorTowerAuxiliaryAxis *>::iterator itA =
|
|
||||||
axes.begin();
|
|
||||||
itA != axes.end(); ++itA) {
|
|
||||||
detectorTowerAuxiliaryAxis *axis = *itA;
|
|
||||||
axis->init();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
detectorTowerAuxiliaryAxis::detectorTowerAuxiliaryAxis(
|
|
||||||
detectorTowerController *pC, int axisNo)
|
|
||||||
: turboPmacAxis(pC, axisNo, false), pC_(pC) {
|
|
||||||
|
|
||||||
/*
|
|
||||||
The superclass constructor sinqAxis calls in turn its superclass constructor
|
|
||||||
asynMotorAxis. In the latter, a pointer to the constructed object this is
|
|
||||||
stored inside the array pAxes_:
|
|
||||||
|
|
||||||
pC->pAxes_[axisNo] = this;
|
|
||||||
|
|
||||||
Therefore, the axes are managed by the controller pC. If axisNo is out of
|
|
||||||
bounds, asynMotorAxis prints an error (see
|
|
||||||
https://github.com/epics-modules/motor/blob/master/motorApp/MotorSrc/asynMotorAxis.cpp,
|
|
||||||
line 40). However, we want the IOC creation to stop completely, since this
|
|
||||||
is a configuration error.
|
|
||||||
*/
|
|
||||||
if (axisNo >= pC->numAxes()) {
|
|
||||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
|
||||||
"Controller \"%s\", axis %d => %s, line %d: FATAL ERROR: "
|
|
||||||
"Axis index %d must be smaller than the total number of axes "
|
|
||||||
"%d",
|
|
||||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
||||||
axisNo_, pC->numAxes());
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register the hook function during construction of the first axis object
|
|
||||||
if (axes.empty()) {
|
|
||||||
initHookRegister(&epicsInithookFunction);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect all axes into this list which will be used in the hook
|
|
||||||
// function
|
|
||||||
axes.push_back(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
detectorTowerAuxiliaryAxis::~detectorTowerAuxiliaryAxis(void) {
|
|
||||||
// Since the controller memory is managed somewhere else, we don't need to
|
|
||||||
// clean up the pointer pC here.
|
|
||||||
}
|
|
||||||
|
|
||||||
asynStatus detectorTowerAuxiliaryAxis::init() {
|
|
||||||
|
|
||||||
// Local variable declaration
|
|
||||||
asynStatus status = asynSuccess;
|
|
||||||
double motorRecResolution = 0.0;
|
|
||||||
|
|
||||||
// The parameter library takes some time to be initialized. Therefore we
|
|
||||||
// wait until the status is not asynParamUndefined anymore.
|
|
||||||
time_t now = time(NULL);
|
|
||||||
time_t maxInitTime = 60;
|
|
||||||
while (1) {
|
|
||||||
status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution(),
|
|
||||||
&motorRecResolution);
|
|
||||||
if (status == asynParamUndefined) {
|
|
||||||
if (now + maxInitTime < time(NULL)) {
|
|
||||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
|
||||||
"Controller \"%s\", axis %d => %s, line "
|
|
||||||
"%d\nInitializing the parameter library failed.\n",
|
|
||||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__,
|
|
||||||
__LINE__);
|
|
||||||
return asynError;
|
|
||||||
}
|
|
||||||
} else if (status == asynSuccess) {
|
|
||||||
break;
|
|
||||||
} else if (status != asynSuccess) {
|
|
||||||
return pC_->paramLibAccessFailed(status, "motorRecResolution_",
|
|
||||||
axisNo_, __PRETTY_FUNCTION__,
|
|
||||||
__LINE__);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return asynSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform the actual poll
|
|
||||||
asynStatus detectorTowerAuxiliaryAxis::doPoll(bool *moving) {
|
|
||||||
|
|
||||||
// Return value for the poll
|
|
||||||
asynStatus poll_status = asynSuccess;
|
|
||||||
|
|
||||||
// Status of parameter library operations
|
|
||||||
asynStatus pl_status = asynSuccess;
|
|
||||||
|
|
||||||
int isMoving = 0;
|
|
||||||
|
|
||||||
// =========================================================================
|
|
||||||
|
|
||||||
/*
|
|
||||||
The axis is moving if the detectorTowerAxis is moving -> We read the moving
|
|
||||||
status from the detectorTowerAxis.
|
|
||||||
*/
|
|
||||||
pl_status = pC_->getIntegerParam(dTA_->axisNo_, pC_->motorStatusMoving(),
|
|
||||||
&isMoving);
|
|
||||||
if (pl_status != asynSuccess) {
|
|
||||||
return pC_->paramLibAccessFailed(pl_status, "motorStatusMoving_",
|
|
||||||
dTA_->axisNo_, __PRETTY_FUNCTION__,
|
|
||||||
__LINE__);
|
|
||||||
}
|
|
||||||
*moving = isMoving != 0;
|
|
||||||
|
|
||||||
// Update the parameter library
|
|
||||||
if (dTA_->error_ != 0) {
|
|
||||||
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
|
|
||||||
if (pl_status != asynSuccess) {
|
|
||||||
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
|
|
||||||
axisNo_, __PRETTY_FUNCTION__,
|
|
||||||
__LINE__);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*moving == false) {
|
|
||||||
pl_status = setIntegerParam(pC_->motorMoveToHome(), 0);
|
|
||||||
if (pl_status != asynSuccess) {
|
|
||||||
return pC_->paramLibAccessFailed(pl_status, "motorMoveToHome_",
|
|
||||||
axisNo_, __PRETTY_FUNCTION__,
|
|
||||||
__LINE__);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pl_status = setIntegerParam(pC_->motorStatusMoving(), *moving);
|
|
||||||
if (pl_status != asynSuccess) {
|
|
||||||
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__);
|
|
||||||
}
|
|
||||||
|
|
||||||
// According to the function documentation of asynMotorAxis::poll, this
|
|
||||||
// function should be called at the end of a poll implementation.
|
|
||||||
pl_status = callParamCallbacks();
|
|
||||||
bool wantToPrint = pl_status != asynSuccess;
|
|
||||||
if (pC_->getMsgPrintControl().shouldBePrinted(
|
|
||||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, wantToPrint,
|
|
||||||
pC_->pasynUser())) {
|
|
||||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
|
||||||
"Controller \"%s\", axis %d => %s, line "
|
|
||||||
"%d:\ncallParamCallbacks failed with %s.%s\n",
|
|
||||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
||||||
pC_->stringifyAsynStatus(poll_status),
|
|
||||||
pC_->getMsgPrintControl().getSuffix());
|
|
||||||
}
|
|
||||||
if (wantToPrint) {
|
|
||||||
poll_status = pl_status;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The limits are written into this class instance inside the doPoll
|
|
||||||
// function of detectorTowerAxis
|
|
||||||
return poll_status;
|
|
||||||
}
|
|
||||||
|
|
||||||
asynStatus detectorTowerAuxiliaryAxis::doMove(double position, int relative,
|
|
||||||
double min_velocity,
|
|
||||||
double max_velocity,
|
|
||||||
double acceleration) {
|
|
||||||
|
|
||||||
double motorRecResolution = 0.0;
|
|
||||||
asynStatus pl_status = pC_->getDoubleParam(
|
|
||||||
axisNo_, pC_->motorRecResolution(), &motorRecResolution);
|
|
||||||
if (pl_status != asynSuccess) {
|
|
||||||
return pC_->paramLibAccessFailed(pl_status, "motorRecResolution_",
|
|
||||||
axisNo_, __PRETTY_FUNCTION__,
|
|
||||||
__LINE__);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Signal to the deferredMovementCollectorLoop (of the detectorTowerAxis)
|
|
||||||
// that a movement should be started to the defined target position.
|
|
||||||
targetPosition_ = position * motorRecResolution;
|
|
||||||
dTA_->receivedTarget_ = true;
|
|
||||||
|
|
||||||
return asynSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
asynStatus detectorTowerAuxiliaryAxis::stop(double acceleration) {
|
|
||||||
|
|
||||||
// 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};
|
|
||||||
|
|
||||||
// =========================================================================
|
|
||||||
|
|
||||||
rw_status = pC_->writeRead(axisNo_, "P350=8", response, 0);
|
|
||||||
|
|
||||||
if (rw_status != asynSuccess) {
|
|
||||||
asynPrint(
|
|
||||||
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
|
||||||
"Controller \"%s\", axis %d => %s, line %d\nStopping the movement "
|
|
||||||
"failed.\n",
|
|
||||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
|
||||||
|
|
||||||
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
|
|
||||||
if (pl_status != asynSuccess) {
|
|
||||||
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
|
|
||||||
axisNo_, __PRETTY_FUNCTION__,
|
|
||||||
__LINE__);
|
|
||||||
}
|
|
||||||
return asynError;
|
|
||||||
}
|
|
||||||
|
|
||||||
return rw_status;
|
|
||||||
}
|
|
||||||
|
|
||||||
asynStatus detectorTowerAuxiliaryAxis::reset() { return dTA_->reset(); };
|
|
@ -1,82 +0,0 @@
|
|||||||
#ifndef detectorTowerAuxiliaryAxis_H
|
|
||||||
#define detectorTowerAuxiliaryAxis_H
|
|
||||||
#include "turboPmacAxis.h"
|
|
||||||
|
|
||||||
// Forward declaration of the controller class to resolve the cyclic dependency
|
|
||||||
// between the controller and the axis .h-file. See
|
|
||||||
// https://en.cppreference.com/w/cpp/language/class.
|
|
||||||
class detectorTowerController;
|
|
||||||
class detectorTowerAxis;
|
|
||||||
|
|
||||||
class detectorTowerAuxiliaryAxis : public turboPmacAxis {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Construct a new detectorTowerAxis
|
|
||||||
*
|
|
||||||
* @param pController Pointer to the associated controller
|
|
||||||
* @param axisNo Index of the axis
|
|
||||||
*/
|
|
||||||
detectorTowerAuxiliaryAxis(detectorTowerController *pController,
|
|
||||||
int axisNo);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Destroy the turboPmacAxis
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
virtual ~detectorTowerAuxiliaryAxis();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Readout of some values from the controller at IOC startup
|
|
||||||
*
|
|
||||||
* @return asynStatus
|
|
||||||
*/
|
|
||||||
asynStatus init();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Implementation of the `stop` function from asynMotorAxis
|
|
||||||
*
|
|
||||||
* @param acceleration Acceleration ACCEL from the motor record. This
|
|
||||||
* value is currently not used.
|
|
||||||
* @return asynStatus
|
|
||||||
*/
|
|
||||||
asynStatus stop(double acceleration);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Implementation of the `doPoll` function from sinqAxis. The
|
|
||||||
* parameters are described in the documentation of `sinqAxis::doPoll`.
|
|
||||||
*
|
|
||||||
* @param moving
|
|
||||||
* @return asynStatus
|
|
||||||
*/
|
|
||||||
asynStatus doPoll(bool *moving);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set the target value for the detector angle and trigger a position
|
|
||||||
* collection cycle
|
|
||||||
*
|
|
||||||
* @param position
|
|
||||||
* @param relative
|
|
||||||
* @param min_velocity
|
|
||||||
* @param max_velocity
|
|
||||||
* @param acceleration
|
|
||||||
* @return asynStatus
|
|
||||||
*/
|
|
||||||
asynStatus doMove(double position, int relative, double min_velocity,
|
|
||||||
double max_velocity, double acceleration);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Call the reset function of the associated `detectorTowerAxis`.
|
|
||||||
*
|
|
||||||
* @return asynStatus
|
|
||||||
*/
|
|
||||||
asynStatus reset();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
detectorTowerController *pC_;
|
|
||||||
detectorTowerAxis *dTA_;
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class detectorTowerAxis;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -8,8 +8,8 @@
|
|||||||
|
|
||||||
#ifndef detectorTowerController_H
|
#ifndef detectorTowerController_H
|
||||||
#define detectorTowerController_H
|
#define detectorTowerController_H
|
||||||
#include "detectorTowerAuxiliaryAxis.h"
|
#include "detectorTowerAngleAxis.h"
|
||||||
#include "detectorTowerAxis.h"
|
#include "detectorTowerLiftAxis.h"
|
||||||
#include "turboPmacController.h"
|
#include "turboPmacController.h"
|
||||||
|
|
||||||
class detectorTowerController : public turboPmacController {
|
class detectorTowerController : public turboPmacController {
|
||||||
@ -19,7 +19,7 @@ class detectorTowerController : public turboPmacController {
|
|||||||
* @brief Construct a new detectorTowerController object
|
* @brief Construct a new detectorTowerController object
|
||||||
*
|
*
|
||||||
* This controller object can handle both "normal" TurboPMAC axes created
|
* This controller object can handle both "normal" TurboPMAC axes created
|
||||||
with the turboPmacAxis constructor and the special detectorTowerAxis.
|
with the turboPmacAxis constructor and the special detectorTowerAngleAxis.
|
||||||
*
|
*
|
||||||
* @param portName See sinqController constructor
|
* @param portName See sinqController constructor
|
||||||
* @param ipPortConfigName See sinqController constructor
|
* @param ipPortConfigName See sinqController constructor
|
||||||
@ -65,51 +65,77 @@ class detectorTowerController : public turboPmacController {
|
|||||||
* @brief Get the axis object
|
* @brief Get the axis object
|
||||||
*
|
*
|
||||||
* @param pasynUser Specify the axis via the asynUser
|
* @param pasynUser Specify the axis via the asynUser
|
||||||
* @return detectorTowerAxis* If no axis could be found, this is a
|
* @return detectorTowerAngleAxis* If no axis could be found, this is
|
||||||
* nullptr
|
* a nullptr
|
||||||
*/
|
*/
|
||||||
detectorTowerAxis *getDetectorTowerAxis(asynUser *pasynUser);
|
detectorTowerAngleAxis *getDetectorTowerAngleAxis(asynUser *pasynUser);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the axis object
|
* @brief Get the axis object
|
||||||
*
|
*
|
||||||
* @param axisNo Specify the axis via its index
|
* @param axisNo Specify the axis via its index
|
||||||
* @return detectorTowerAxis* If no axis could be found, this is a
|
* @return detectorTowerAngleAxis* If no axis could be found, this is
|
||||||
* nullptr
|
* a nullptr
|
||||||
*/
|
*/
|
||||||
detectorTowerAxis *getDetectorTowerAxis(int axisNo);
|
detectorTowerAngleAxis *getDetectorTowerAngleAxis(int axisNo);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the axis object
|
* @brief Get the axis object
|
||||||
*
|
*
|
||||||
* @param pasynUser Specify the axis via the asynUser
|
* @param pasynUser Specify the axis via the asynUser
|
||||||
* @return detectorTowerAuxiliaryAxis* If no axis could be found,
|
* @return detectorTowerLiftAxis* If no axis could be found,
|
||||||
* this is a nullptr
|
* this is a nullptr
|
||||||
*/
|
*/
|
||||||
detectorTowerAuxiliaryAxis *
|
detectorTowerLiftAxis *getDetectorTowerLiftAxis(asynUser *pasynUser);
|
||||||
getDetectorTowerAuxiliaryAxis(asynUser *pasynUser);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the axis object
|
* @brief Get the axis object
|
||||||
*
|
*
|
||||||
* @param axisNo Specify the axis via its index
|
* @param axisNo Specify the axis via its index
|
||||||
* @return detectorTowerAuxiliaryAxis* If no axis could be found,
|
* @return detectorTowerLiftAxis* If no axis could be found,
|
||||||
* this is a nullptr
|
* this is a nullptr
|
||||||
*/
|
*/
|
||||||
detectorTowerAuxiliaryAxis *getDetectorTowerAuxiliaryAxis(int axisNo);
|
detectorTowerLiftAxis *getDetectorTowerLiftAxis(int axisNo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Common poll function for both lift and angle axes
|
||||||
|
*
|
||||||
|
* @param moving
|
||||||
|
* @param angleAxis
|
||||||
|
* @param liftAxis
|
||||||
|
* @return asynStatus
|
||||||
|
*/
|
||||||
|
asynStatus pollDetectorAxes(bool *moving, detectorTowerAngleAxis *angleAxis,
|
||||||
|
detectorTowerLiftAxis *liftAxis);
|
||||||
|
|
||||||
// Accessors for additional PVs
|
// Accessors for additional PVs
|
||||||
int positionStateRBV() { return positionStateRBV_; }
|
int positionStateRBV() { return positionStateRBV_; }
|
||||||
int changeState() { return changeState_; }
|
int changeState() { return changeState_; }
|
||||||
int motorTargetOffset() { return motorTargetOffset_; }
|
int changeStateRBV() { return changeStateRBV_; }
|
||||||
|
int motorOrigin() { return motorOrigin_; }
|
||||||
|
int motorAdjustOrigin() { return motorAdjustOrigin_; }
|
||||||
|
int motorAdjustOriginHighLimitFromDriver() {
|
||||||
|
return motorAdjustOriginHighLimitFromDriver_;
|
||||||
|
}
|
||||||
|
int motorAdjustOriginLowLimitFromDriver() {
|
||||||
|
return motorAdjustOriginLowLimitFromDriver_;
|
||||||
|
}
|
||||||
|
int liftSupportOrigin() { return liftSupportOrigin_; }
|
||||||
|
int liftSupportAdjustOrigin() { return liftSupportAdjustOrigin_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Indices of additional PVs
|
// Indices of additional PVs
|
||||||
#define FIRST_detectorTower_PARAM positionStateRBV_
|
#define FIRST_detectorTower_PARAM positionStateRBV_
|
||||||
int positionStateRBV_;
|
int positionStateRBV_;
|
||||||
int changeState_;
|
int changeState_;
|
||||||
int motorTargetOffset_;
|
int changeStateRBV_;
|
||||||
#define LAST_detectorTower_PARAM motorTargetOffset_
|
int motorOrigin_;
|
||||||
|
int motorAdjustOrigin_;
|
||||||
|
int motorAdjustOriginHighLimitFromDriver_;
|
||||||
|
int motorAdjustOriginLowLimitFromDriver_;
|
||||||
|
int liftSupportOrigin_;
|
||||||
|
int liftSupportAdjustOrigin_;
|
||||||
|
#define LAST_detectorTower_PARAM liftSupportAdjustOrigin_
|
||||||
};
|
};
|
||||||
#define NUM_detectorTower_DRIVER_PARAMS \
|
#define NUM_detectorTower_DRIVER_PARAMS \
|
||||||
(&LAST_detectorTower_PARAM - &FIRST_detectorTower_PARAM + 1)
|
(&LAST_detectorTower_PARAM - &FIRST_detectorTower_PARAM + 1)
|
||||||
|
308
src/detectorTowerLiftAxis.cpp
Normal file
308
src/detectorTowerLiftAxis.cpp
Normal file
@ -0,0 +1,308 @@
|
|||||||
|
#include "detectorTowerLiftAxis.h"
|
||||||
|
#include "detectorTowerAngleAxis.h"
|
||||||
|
#include "detectorTowerController.h"
|
||||||
|
#include "turboPmacController.h"
|
||||||
|
#include <epicsExport.h>
|
||||||
|
#include <errlog.h>
|
||||||
|
#include <iocsh.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
Contains all instances of detectorTowerLiftAxis which have been created and
|
||||||
|
is used in the initialization hook function.
|
||||||
|
*/
|
||||||
|
static std::vector<detectorTowerLiftAxis *> axes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Hook function to perform certain actions during the IOC initialization
|
||||||
|
*
|
||||||
|
* @param iState
|
||||||
|
*/
|
||||||
|
static void epicsInithookFunction(initHookState iState) {
|
||||||
|
if (iState == initHookAfterDatabaseRunning) {
|
||||||
|
// Iterate through all axes of each and call the initialization method
|
||||||
|
// on each one of them.
|
||||||
|
for (std::vector<detectorTowerLiftAxis *>::iterator itA = axes.begin();
|
||||||
|
itA != axes.end(); ++itA) {
|
||||||
|
detectorTowerLiftAxis *axis = *itA;
|
||||||
|
axis->init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
detectorTowerLiftAxis::detectorTowerLiftAxis(detectorTowerController *pC,
|
||||||
|
int axisNo)
|
||||||
|
: turboPmacAxis(pC, axisNo, false), pC_(pC) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
The superclass constructor sinqAxis calls in turn its superclass constructor
|
||||||
|
asynMotorAxis. In the latter, a pointer to the constructed object this is
|
||||||
|
stored inside the array pAxes_:
|
||||||
|
|
||||||
|
pC->pAxes_[axisNo] = this;
|
||||||
|
|
||||||
|
Therefore, the axes are managed by the controller pC. If axisNo is out of
|
||||||
|
bounds, asynMotorAxis prints an error (see
|
||||||
|
https://github.com/epics-modules/motor/blob/master/motorApp/MotorSrc/asynMotorAxis.cpp,
|
||||||
|
line 40). However, we want the IOC creation to stop completely, since this
|
||||||
|
is a configuration error.
|
||||||
|
*/
|
||||||
|
if (axisNo >= pC->numAxes()) {
|
||||||
|
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
|
"Controller \"%s\", axis %d => %s, line %d: FATAL ERROR: "
|
||||||
|
"Axis index %d must be smaller than the total number of axes "
|
||||||
|
"%d",
|
||||||
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||||
|
axisNo_, pC->numAxes());
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register the hook function during construction of the first axis object
|
||||||
|
if (axes.empty()) {
|
||||||
|
initHookRegister(&epicsInithookFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect all axes into this list which will be used in the hook
|
||||||
|
// function
|
||||||
|
axes.push_back(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
detectorTowerLiftAxis::~detectorTowerLiftAxis(void) {
|
||||||
|
// Since the controller memory is managed somewhere else, we don't need to
|
||||||
|
// clean up the pointer pC here.
|
||||||
|
}
|
||||||
|
|
||||||
|
asynStatus detectorTowerLiftAxis::init() {
|
||||||
|
|
||||||
|
// Local variable declaration
|
||||||
|
asynStatus status = asynSuccess;
|
||||||
|
double motorRecResolution = 0.0;
|
||||||
|
|
||||||
|
// The parameter library takes some time to be initialized. Therefore we
|
||||||
|
// wait until the status is not asynParamUndefined anymore.
|
||||||
|
time_t now = time(NULL);
|
||||||
|
time_t maxInitTime = 60;
|
||||||
|
while (1) {
|
||||||
|
status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution(),
|
||||||
|
&motorRecResolution);
|
||||||
|
if (status == asynParamUndefined) {
|
||||||
|
if (now + maxInitTime < time(NULL)) {
|
||||||
|
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
|
"Controller \"%s\", axis %d => %s, line "
|
||||||
|
"%d\nInitializing the parameter library failed.\n",
|
||||||
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__,
|
||||||
|
__LINE__);
|
||||||
|
return asynError;
|
||||||
|
}
|
||||||
|
} else if (status == asynSuccess) {
|
||||||
|
break;
|
||||||
|
} else if (status != asynSuccess) {
|
||||||
|
return pC_->paramLibAccessFailed(status, "motorRecResolution_",
|
||||||
|
axisNo_, __PRETTY_FUNCTION__,
|
||||||
|
__LINE__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the motorStatusMoving flag
|
||||||
|
status = setIntegerParam(pC_->motorStatusMoving(), 0);
|
||||||
|
if (status != asynSuccess) {
|
||||||
|
return pC_->paramLibAccessFailed(status, "motorStatusMoving_", axisNo_,
|
||||||
|
__PRETTY_FUNCTION__, __LINE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
return asynSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform the actual poll
|
||||||
|
asynStatus detectorTowerLiftAxis::poll(bool *moving) {
|
||||||
|
asynStatus status = asynSuccess;
|
||||||
|
|
||||||
|
// Is this axis the one with the smallest index?
|
||||||
|
// If not, just read out the movement status and update *moving
|
||||||
|
if (axisNo() < angleAxis_->axisNo()) {
|
||||||
|
status = pC_->pollDetectorAxes(moving, angleAxis_, this);
|
||||||
|
} else {
|
||||||
|
status = pC_->getIntegerParam(axisNo(), pC_->motorStatusMoving(),
|
||||||
|
(int *)moving);
|
||||||
|
if (status != asynSuccess) {
|
||||||
|
return pC_->paramLibAccessFailed(status, "motorStatusMoving",
|
||||||
|
axisNo_, __PRETTY_FUNCTION__,
|
||||||
|
__LINE__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wasMoving_ = *moving;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
asynStatus detectorTowerLiftAxis::doPoll(bool *moving) {
|
||||||
|
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
|
"Controller \"%s\", axis %d => %s, line %d\nThe doPoll method "
|
||||||
|
"of this axis type should not be reachable. This is a bug.\n",
|
||||||
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
||||||
|
return asynError;
|
||||||
|
}
|
||||||
|
|
||||||
|
asynStatus detectorTowerLiftAxis::doMove(double position, int relative,
|
||||||
|
double min_velocity,
|
||||||
|
double max_velocity,
|
||||||
|
double acceleration) {
|
||||||
|
|
||||||
|
double motorRecResolution = 0.0;
|
||||||
|
asynStatus plStatus = pC_->getDoubleParam(
|
||||||
|
axisNo_, pC_->motorRecResolution(), &motorRecResolution);
|
||||||
|
if (plStatus != asynSuccess) {
|
||||||
|
return pC_->paramLibAccessFailed(plStatus, "motorRecResolution_",
|
||||||
|
axisNo_, __PRETTY_FUNCTION__,
|
||||||
|
__LINE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signal to the deferredMovementCollectorLoop (of the
|
||||||
|
// detectorTowerAngleAxis) that a movement should be started to the defined
|
||||||
|
// target position.
|
||||||
|
targetPosition_ = position * motorRecResolution;
|
||||||
|
angleAxis_->receivedTarget_ = true;
|
||||||
|
|
||||||
|
return asynSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
asynStatus detectorTowerLiftAxis::stop(double acceleration) {
|
||||||
|
|
||||||
|
// Status of read-write-operations of ASCII commands to the controller
|
||||||
|
asynStatus rwStatus = asynSuccess;
|
||||||
|
|
||||||
|
// Status of parameter library operations
|
||||||
|
asynStatus plStatus = asynSuccess;
|
||||||
|
|
||||||
|
char response[pC_->MAXBUF_] = {0};
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
rwStatus = pC_->writeRead(axisNo_, "P350=8", response, 0);
|
||||||
|
|
||||||
|
if (rwStatus != asynSuccess) {
|
||||||
|
asynPrint(
|
||||||
|
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
|
"Controller \"%s\", axis %d => %s, line %d\nStopping the movement "
|
||||||
|
"failed.\n",
|
||||||
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
||||||
|
|
||||||
|
plStatus = setIntegerParam(pC_->motorStatusProblem(), true);
|
||||||
|
if (plStatus != asynSuccess) {
|
||||||
|
return pC_->paramLibAccessFailed(plStatus, "motorStatusProblem_",
|
||||||
|
axisNo_, __PRETTY_FUNCTION__,
|
||||||
|
__LINE__);
|
||||||
|
}
|
||||||
|
return asynError;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rwStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
asynStatus detectorTowerLiftAxis::reset() { return angleAxis_->reset(); };
|
||||||
|
|
||||||
|
asynStatus detectorTowerLiftAxis::adjustOrigin(double newOrigin) {
|
||||||
|
|
||||||
|
asynStatus status = asynSuccess;
|
||||||
|
char command[pC_->MAXBUF_] = {0};
|
||||||
|
char response[pC_->MAXBUF_] = {0};
|
||||||
|
int positionState = 0;
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
status =
|
||||||
|
pC_->getIntegerParam(axisNo_, pC_->positionStateRBV(), &positionState);
|
||||||
|
if (status != asynSuccess) {
|
||||||
|
return pC_->paramLibAccessFailed(status, "positionStateRBV_", axisNo_,
|
||||||
|
__PRETTY_FUNCTION__, __LINE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the axis is in changer position, it must be moved into working
|
||||||
|
// position before any move can be started.
|
||||||
|
bool isInChangerPos = positionState == 2 || positionState == 3;
|
||||||
|
if (pC_->getMsgPrintControl().shouldBePrinted(
|
||||||
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||||
|
isInChangerPos, pC_->pasynUser())) {
|
||||||
|
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
|
"Controller \"%s\", axis %d => %s, line %d\nAxis cannot be "
|
||||||
|
"moved because it is moving from working to changer "
|
||||||
|
"position, is in changer position or is moving from changer "
|
||||||
|
"to working position.%s\n",
|
||||||
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||||
|
pC_->getMsgPrintControl().getSuffix());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the new adjust for the lift axis
|
||||||
|
snprintf(command, sizeof(command), "Q356=%lf P350=5", newOrigin);
|
||||||
|
|
||||||
|
// We don't expect an answer
|
||||||
|
status = pC_->writeRead(axisNo_, command, response, 0);
|
||||||
|
|
||||||
|
if (status != asynSuccess) {
|
||||||
|
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
|
"Controller \"%s\", axis %d => %s, line %d\nSetting new "
|
||||||
|
"lift origin %lf failed.\n",
|
||||||
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||||
|
newOrigin);
|
||||||
|
status = setIntegerParam(pC_->motorStatusProblem(), true);
|
||||||
|
if (status != asynSuccess) {
|
||||||
|
return pC_->paramLibAccessFailed(status, "motorStatusProblem_",
|
||||||
|
axisNo_, __PRETTY_FUNCTION__,
|
||||||
|
__LINE__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
asynStatus detectorTowerLiftAxis::adjustSupportOrigin(double newOrigin) {
|
||||||
|
|
||||||
|
asynStatus status = asynSuccess;
|
||||||
|
char command[pC_->MAXBUF_] = {0};
|
||||||
|
char response[pC_->MAXBUF_] = {0};
|
||||||
|
int positionState = 0;
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
status =
|
||||||
|
pC_->getIntegerParam(axisNo(), pC_->positionStateRBV(), &positionState);
|
||||||
|
if (status != asynSuccess) {
|
||||||
|
return pC_->paramLibAccessFailed(status, "positionStateRBV_", axisNo_,
|
||||||
|
__PRETTY_FUNCTION__, __LINE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the axis is in changer position, it must be moved into working
|
||||||
|
// position before any move can be started.
|
||||||
|
bool isInChangerPos = positionState == 2 || positionState == 3;
|
||||||
|
if (pC_->getMsgPrintControl().shouldBePrinted(
|
||||||
|
pC_->portName, axisNo(), __PRETTY_FUNCTION__, __LINE__,
|
||||||
|
isInChangerPos, pC_->pasynUser())) {
|
||||||
|
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
|
"Controller \"%s\", axis %d => %s, line %d\nAxis cannot be "
|
||||||
|
"moved because it is moving from working to changer "
|
||||||
|
"position, is in changer position or is moving from changer "
|
||||||
|
"to working position.%s\n",
|
||||||
|
pC_->portName, axisNo(), __PRETTY_FUNCTION__, __LINE__,
|
||||||
|
pC_->getMsgPrintControl().getSuffix());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the new adjust for the lift axis
|
||||||
|
snprintf(command, sizeof(command), "Q656=%lf P350=6", newOrigin);
|
||||||
|
|
||||||
|
// We don't expect an answer
|
||||||
|
status = pC_->writeRead(axisNo_, command, response, 0);
|
||||||
|
|
||||||
|
if (status != asynSuccess) {
|
||||||
|
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
|
"Controller \"%s\", axis %d => %s, line %d\nSetting new "
|
||||||
|
"lift origin %lf failed.\n",
|
||||||
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||||
|
newOrigin);
|
||||||
|
status = setIntegerParam(pC_->motorStatusProblem(), true);
|
||||||
|
if (status != asynSuccess) {
|
||||||
|
return pC_->paramLibAccessFailed(status, "motorStatusProblem_",
|
||||||
|
axisNo_, __PRETTY_FUNCTION__,
|
||||||
|
__LINE__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
117
src/detectorTowerLiftAxis.h
Normal file
117
src/detectorTowerLiftAxis.h
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
#ifndef detectorTowerLiftAxis_H
|
||||||
|
#define detectorTowerLiftAxis_H
|
||||||
|
#include "turboPmacAxis.h"
|
||||||
|
|
||||||
|
// Forward declaration of the controller class to resolve the cyclic dependency
|
||||||
|
// between the controller and the axis .h-file. See
|
||||||
|
// https://en.cppreference.com/w/cpp/language/class.
|
||||||
|
class detectorTowerController;
|
||||||
|
class detectorTowerAngleAxis;
|
||||||
|
|
||||||
|
class detectorTowerLiftAxis : public turboPmacAxis {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Construct a new detectorTowerAngleAxis
|
||||||
|
*
|
||||||
|
* @param pController Pointer to the associated controller
|
||||||
|
* @param axisNo Index of the axis
|
||||||
|
*/
|
||||||
|
detectorTowerLiftAxis(detectorTowerController *pController, int axisNo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Destroy the turboPmacAxis
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
virtual ~detectorTowerLiftAxis();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Readout of some values from the controller at IOC startup
|
||||||
|
*
|
||||||
|
* @return asynStatus
|
||||||
|
*/
|
||||||
|
asynStatus init();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Implementation of the `stop` function from asynMotorAxis
|
||||||
|
*
|
||||||
|
* @param acceleration Acceleration ACCEL from the motor record. This
|
||||||
|
* value is currently not used.
|
||||||
|
* @return asynStatus
|
||||||
|
*/
|
||||||
|
asynStatus stop(double acceleration);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Poll all detector tower axes, if this axis is the one with the
|
||||||
|
* smallest index.
|
||||||
|
*
|
||||||
|
* We do not use the doPoll framework from sinqMotor here on purpose, since
|
||||||
|
* we want to e.g. reset the motorStatusProblem for all axes at once at the
|
||||||
|
* beginning of the poll.
|
||||||
|
*
|
||||||
|
* @param moving
|
||||||
|
* @return asynStatus
|
||||||
|
*/
|
||||||
|
asynStatus poll(bool *moving);
|
||||||
|
asynStatus doPoll(bool *moving);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the target value for the detector angle and trigger a position
|
||||||
|
* collection cycle
|
||||||
|
*
|
||||||
|
* @param position
|
||||||
|
* @param relative
|
||||||
|
* @param min_velocity
|
||||||
|
* @param max_velocity
|
||||||
|
* @param acceleration
|
||||||
|
* @return asynStatus
|
||||||
|
*/
|
||||||
|
asynStatus doMove(double position, int relative, double min_velocity,
|
||||||
|
double max_velocity, double acceleration);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Call the reset function of the associated
|
||||||
|
* `detectorTowerAngleAxis`.
|
||||||
|
*
|
||||||
|
* @return asynStatus
|
||||||
|
*/
|
||||||
|
asynStatus reset();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Move the axis to the position `newOrigin` and recalibrate
|
||||||
|
*
|
||||||
|
* When calling this function, the lift axis moves to `newOrigin` and the
|
||||||
|
* hardware sets this position as the new origin.
|
||||||
|
*
|
||||||
|
* @param origin
|
||||||
|
* @return asynStatus
|
||||||
|
*/
|
||||||
|
asynStatus adjustOrigin(double newOrigin);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Move the support motor of the beam lift to the position
|
||||||
|
* `newOrigin` and recalibrate
|
||||||
|
*
|
||||||
|
* When calling this function, the lift axis support motor moves to
|
||||||
|
* `newOrigin` and the hardware sets this position as the new origin.
|
||||||
|
*
|
||||||
|
* @param origin
|
||||||
|
* @return asynStatus
|
||||||
|
*/
|
||||||
|
asynStatus adjustSupportOrigin(double newOrigin);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return a pointer to the associated angle axis
|
||||||
|
*
|
||||||
|
* @return detectorTowerAngleAxis*
|
||||||
|
*/
|
||||||
|
detectorTowerAngleAxis *angleAxis() { return angleAxis_; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
detectorTowerController *pC_;
|
||||||
|
detectorTowerAngleAxis *angleAxis_;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class detectorTowerAngleAxis;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
1
turboPmac
Submodule
1
turboPmac
Submodule
Submodule turboPmac added at 5298b5ef69
Reference in New Issue
Block a user