211 lines
11 KiB
Markdown
211 lines
11 KiB
Markdown
# detectorTower
|
|
|
|
## <span style="color:red">Please read the documentation of sinqMotor first:https://git.psi.ch/sinq-epics-modules/sinqmotor</span>
|
|
|
|
## Overview
|
|
|
|

|
|
|
|
This is a driver for the detector tower which is based on the Turbo PMAC driver
|
|
(https://gitea.psi.ch/lin-epics-modules/turboPmac). It consists of the following
|
|
four objects:
|
|
- `detectorTowerController`: This is an extended 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`.
|
|
- `detectorTowerAngleAxis`: This is a virtual axis which controls multiple
|
|
physical motors ($x$ and $z$) in order to provide a combined movement. Moving it
|
|
results in a rotation of the entire beam around the support axis position ($\alpha$).
|
|
- `detectorTowerLiftAxis`: This is a virtual axis which controls multiple
|
|
physical motors in order to provide a combined movement. Moving it results in a
|
|
vertical lift ($z_{lift}$).
|
|
- `detectorTowerSupportAxis`: This is an axis at the rotation center of the beam
|
|
which is part of the combined movements. Its origin can be shifted manually for
|
|
small adjustments, resulting in a corresponding movement. Other than that, it is
|
|
not meant to move on its own, hence setting a new value to the `VAL` field of
|
|
the motor record won't cause it to move.
|
|
|
|
## User guide
|
|
|
|
The centerpiece of this driver is the `detectorTowerAngleAxis`, which controls
|
|
the angle of the detector flight tube. It is supported by two secondary axes,
|
|
`detectorTowerLiftAxis` and `detectorTowerSupportAxis`. All three axes are
|
|
created by a single IOC shell command `detectorTowerAxis` (see
|
|
[Usage in IOC shell](#usage-in-ioc-shell)). All three axes use absolute encoders
|
|
and therefore cannot perform a reference / home drive.
|
|
|
|
The first two axes can be moved independently from each other or together as a
|
|
combined movement by issuing both move orders within a certain time span. This
|
|
time span can be configured via the IOC shell function `setDeferredMovementWait`
|
|
and defaults to 100 ms. This allows the user to start a combined movement e.g.
|
|
via caput:
|
|
|
|
```bash
|
|
caput $(ControllerPV):angle 2 & $(ControllerPV):caput lift 10
|
|
```
|
|
which moves the angle to 2 degree and shifts the entire beam vertically by 10 mm.
|
|
When using the axis from NICOS, using the `maw` or `move` command with multiple
|
|
devices has the same effect:
|
|
|
|
```bash
|
|
move("angle", 2, "lift", 10)
|
|
```
|
|
|
|
If one axis is already moving, no new move command can be issued until the
|
|
movement is finished. The `detectorTowerSupportAxis` cannot be moved directly.
|
|
|
|
It is possible to shift the origin of all three axes (and therefore also moving
|
|
the `detectorTowerSupportAxis`) for small adjustments. The current origin
|
|
position in the axis unit (degree for `detectorTowerAngleAxis`, mm for
|
|
`detectorTowerLiftAxis` and encoder steps for `detectorTowerSupportAxis`) can be
|
|
read from the PV `$(INSTR)$(M):Origin`. The origin can be shifted by the amount
|
|
of axis units written to `$(INSTR)$(M):AdjustOrigin`. Since this is a relative
|
|
movement, writing "10" two times to `$(INSTR)$(M):AdjustOrigin` shifts the
|
|
origin by 20 axis units. Therefore, the limits are only there to prevent too
|
|
large shifts during a single write (since the origin can be shifted to any
|
|
position by writing to `$(INSTR)$(M):AdjustOrigin` repeatedly).
|
|
|
|
The detector tower can be moved into a so-called "changer position" by writing
|
|
"1" to the PV `$(INSTR)$(M):ChangeState`. This causes the entire tower to move
|
|
vertically down. When it is in the changer position, the axes cannot be moved at
|
|
all. In order to go back to the "working state" where the axes can be moved
|
|
again, write "0" to `$(INSTR)$(M):ChangeState`. The current state of the axis
|
|
can be read out from `$(INSTR)$(M):ChangingStateRBV`. In case starting a change
|
|
movement succeeds, `$(INSTR)$(M):ChangeStateRBV` changes its value accordingly,
|
|
otherwise it stays at its current value.
|
|
|
|
The utilities provided in the `utils` folder in `turboPmac/utils` work with this
|
|
driver as well.
|
|
|
|
### Usage in IOC shell
|
|
|
|
detectorTower exports the following IOC shell functions:
|
|
- `detectorTowerController`: Create a new controller object.
|
|
- `detectorTowerAxis`: Create a `detectorTowerAngleAxis`, a `detectorTowerLiftAxis`
|
|
and a `detectorTowerSupportAxis` object and link them to each other
|
|
|
|
The constructor function for `detectorTowerAxis` has the following syntax:
|
|
|
|
```bash
|
|
detectorTowerAngleAxis("$(NAME)",1, 2, 3)
|
|
```
|
|
|
|
with 1 being the axis number / index of the `detectorTowerAngleAxis`, 2 being
|
|
the `detectorTowerLiftAxis` and 3 being the `detectorTowerSupportAxis`. These
|
|
axes are parametrized in the same way as any "normal" axes via a substitution
|
|
file (see corresponding section below).
|
|
|
|
"Normal" `turboPmacAxis` may be used together with this controller:
|
|
|
|
```bash
|
|
# Define the name of the controller and the corresponding port
|
|
epicsEnvSet("NAME","mcu")
|
|
epicsEnvSet("ASYN_PORT","p$(NAME)")
|
|
|
|
# Create the TCP/IP socket used to talk with the controller. The socket can beadressed from within the IOC shell via the port name
|
|
drvAsynIPPortConfigure("$(ASYN_PORT)","172.28.101.24:1025")
|
|
|
|
# Create the controller object with the defined name and connect it to the socket via the port name.
|
|
# The other parameters are as follows:
|
|
# 8: Maximum number of axes
|
|
# 0.05: Busy poll period in seconds
|
|
# 1: Idle poll period in seconds
|
|
# 1: Socket communication timeout in seconds
|
|
detectorTowerController("$(NAME)", "$(ASYN_PORT)", 8, 0.05, 1, 1);
|
|
|
|
# Slot 1, 2 and 3 are occupied by the three detector tower axis, while the slots 4 and 5 are "normal" Turbo PMAC axes.
|
|
detectorTowerAngleAxis("$(NAME)",1, 2, 3);
|
|
turboPmacAxis("$(NAME)",4);
|
|
turboPmacAxis("$(NAME)",5);
|
|
|
|
# Set the number of subsequent timeouts
|
|
setMaxSubsequentTimeouts("$(NAME)", 20);
|
|
|
|
# Configure the timeout frequency watchdog:
|
|
setThresholdComTimeout("$(NAME)", 100, 1);
|
|
|
|
# Parametrize the EPICS record database with the substitution file named after the MCU.
|
|
# Since this driver is based on Turbo PMAC, we need to parametrize turboPmac.db in addition to sinqMotor.db and detectorTower.db.
|
|
epicsEnvSet("SINQDBPATH","$(detectorTower_DB)/sinqMotor.db")
|
|
dbLoadTemplate("$(TOP)/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$(NAME)")
|
|
epicsEnvSet("SINQDBPATH","$(detectorTower_DB)/turboPmac.db")
|
|
dbLoadTemplate("$(TOP)/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$(NAME)")
|
|
epicsEnvSet("SINQDBPATH","$(detectorTower_DB)/detectorTower.db")
|
|
dbLoadTemplate("$(TOP)/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$(NAME)")
|
|
dbLoadRecords("$(sinqMotor_DB)/asynRecord.db","P=$(INSTR)$(NAME),PORT=$(ASYN_PORT)")
|
|
```
|
|
|
|
### Substitution file
|
|
|
|
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:
|
|
|
|
```bash
|
|
detectorTowerAngleAxis("$(NAME)",1, 2, 3);
|
|
turboPmacAxis("$(NAME)",4);
|
|
turboPmacAxis("$(NAME)",5);
|
|
```
|
|
```bash
|
|
file "$(SINQDBPATH)"
|
|
{
|
|
pattern
|
|
{ AXIS, M, DESC, EGU, DIR, MRES, MSGTEXTSIZE, ENABLEMOVWATCHDOG, LIMITSOFFSET, CANSETSPEED }
|
|
{ 1, "angle", "Angle of the beam guide", degree, Pos, 0.001, 200, 0, 1.0, 0 }
|
|
{ 2, "lift", "Detector vertical lift offset", mm, Pos, 0.001, 200, 0, 1.0, 0 }
|
|
{ 3, "suppoer", "Support axis", mm, Pos, 0.001, 200, 0, 1.0, 0 }
|
|
{ 4, "other axis A", "A normal axis", degree, Pos, 0.001, 200, 1, 2.0, 1 }
|
|
{ 5, "other axis B", "Another normal axis", degree, Pos, 0.001, 200, 1, 2.0, 0 }
|
|
}
|
|
```
|
|
|
|
Note that the speed of the detector tower axes 1, 2 and 3 cannot be set. Setting
|
|
`CANSETSPEED` to 1 does not change this behaviour.
|
|
|
|
## Developer guide
|
|
|
|
### Code architecture
|
|
|
|
The code is designed around the `detectorTowerAngleAxis`, which controls the
|
|
angle of the beam guide and acts as a "master" axis which contains pointers to
|
|
its attached `detectorTowerLiftAxis` and `detectorTowerSupportAxis`. All three
|
|
axes are polled at once via the function `detectorTowerController::pollDetectorAxes`,
|
|
which is called from the individual `poll` functions of the axes (the `doPoll`
|
|
mechanism from `sinqMotor` is not used). To avoid polling the axes multiple
|
|
times during one controller cycle, the function `detectorTowerController::pollDetectorAxes`
|
|
is only executed for the axis with the smallest index. Since this axis is polled
|
|
first, the other two axes are therefore already up-to-date when they execute
|
|
their own poll function. If one of the axes is moving, all three axes are marked
|
|
as moving. The same is true for errors.
|
|
|
|
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 `detectorTowerAngleAxis->deferredMovementWait_` seconds
|
|
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.
|
|
`detectorTowerAngleAxis->deferredMovementWait_` can be set with the IOC shell
|
|
function `setDeferredMovementWait` and defaults to 100 ms.
|
|
|
|
The `detectorTowerController` is a thin wrapper around a `turboPmacController`
|
|
which overwrites the `readInt32`, `writeInt32` and `writeFloat64` methods in
|
|
order to support the additional PVs defined in `db/detectorTower.db`. Any calls
|
|
to these two methods not concerning the aforementioned PVs are directly
|
|
forwarded to `turboPmacController::readInt32` / `turboPmacController::writeInt32`.
|
|
|
|
### Versioning
|
|
|
|
Please see the documentation for the module sinqMotor:
|
|
https://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/main/README.md.
|
|
|
|
### How to build it
|
|
|
|
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. |