Current draft for the detector axis tower driver

This commit is contained in:
2025-03-04 16:38:00 +01:00
parent 8b185c6260
commit 747d08c52a
8 changed files with 718 additions and 308 deletions

View File

@ -8,11 +8,13 @@ ARCH_FILTER=RHEL%
# Additional module dependencies # Additional module dependencies
REQUIRED+=motorBase REQUIRED+=motorBase
REQUIRED+=turboPmac
# Specify the version of motorBase we want to build against # Specify the version of motorBase we want to build against
motorBase_VERSION=7.2.2 motorBase_VERSION=7.2.2
# Specify the version of sinqMotor we want to build against
sinqMotor_VERSION=mathis_s
# Specify the version of turboPmac we want to build against # Specify the version of turboPmac we want to build against
turboPmac_VERSION=mathis_s turboPmac_VERSION=mathis_s

View File

@ -1,3 +1,66 @@
# detectorTower # detectorTower
TODO! 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 two objects:
- `detectorTowerAxis`: 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`.
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
This driver is a standard sinqMotor-derived driver and does not need any specific configuration. For the general configuration, please see https://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/main/README.md. The utilities provided in the `utils` folder of https://git.psi.ch/sinq-epics-modules/turboPmac work with this driver as well.
## Developer guide
### Usage in IOC shell
detectorTower exports the following IOC shell functions:
- `detectorTowerController`: Create a new controller object.
- `detectorTowerAxis`: Create a new axis object.
As mentioned above, also "normal" `turboPmacAxis` may be used together with this controller:
```
# 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 be adressed 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 is occupied by a detector tower axis, while the slots 2 and 3 are "normal" Turbo PMAC axes.
detectorTowerAxis("$(NAME)",1);
turboPmacAxis("$(NAME)",2);
turboPmacAxis("$(NAME)",3);
# 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","$(sinqMotor_DB)/sinqMotor.db")
dbLoadTemplate("$(TOP)/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$(NAME)")
epicsEnvSet("SINQDBPATH","$(turboPmac_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)")
```
### 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
Please see the documentation for the module sinqMotor: https://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/main/README.md.

View File

@ -1,70 +0,0 @@
# Reset any errors of the virtual axis.
# This record is coupled to the parameter library via reset_ -> RESET.
record(longout, "$(INSTR)$(M):Reset") {
field(DTYP, "asynInt32")
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) RESET")
field(PINI, "NO")
}
# Read the position state of the axis:
# 0 = not ready
# 1 = ready (in working position)
# 2 = ready (in changer position)
record(longout, "$(INSTR)$(M):PositionState")
{
field(DTYP, "asynInt32")
field(INP, "@asyn($(CONTROLLER),$(AXIS)) POSITION_STATE")
field(SCAN, "I/O Intr")
}
# Set the target position for the offset of the lift (motor COZ)
# This record is coupled to the parameter library via liftOffset_ -> LIFT_OFFSET.
record(ao, "$(INSTR)$(M):LiftOffset") {
field(DTYP, "asynFloat64")
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) LIFT_OFFSET")
field(PINI, "NO")
}
# Set the target position for the offset of the lift (motor COZ)
# This record is coupled to the parameter library via liftOffset_ -> LIFT_OFFSET.
record(ao, "$(INSTR)$(M):LiftOffset") {
field(DTYP, "asynFloat64")
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) LIFT_OFFSET")
field(PINI, "NO")
}
# Read the lower and upper limits for the lift offset
record(ai, "$(INSTR)$(M):LiftOffsetLowLimitRBV")
{
field(DTYP, "asynFloat64")
field(INP, "@asyn($(CONTROLLER),$(AXIS)) LIFT_OFFSET_LOW_LIMIT")
field(SCAN, "I/O Intr")
}
record(ai, "$(INSTR)$(M):LiftOffsetHighLimitRBV")
{
field(DTYP, "asynFloat64")
field(INP, "@asyn($(CONTROLLER),$(AXIS)) LIFT_OFFSET_HIGH_LIMIT")
field(SCAN, "I/O Intr")
}
# Set the target position for the offset of the detector tilt (motor COX)
# This record is coupled to the parameter library via tiltOffset_ -> TILT_OFFSET.
record(ao, "$(INSTR)$(M):TiltOffset") {
field(DTYP, "asynFloat64")
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) TILT_OFFSET")
field(PINI, "NO")
}
# Read the lower and upper limits for the detector tilt offset
record(ai, "$(INSTR)$(M):TiltOffsetLowLimitRBV")
{
field(DTYP, "asynFloat64")
field(INP, "@asyn($(CONTROLLER),$(AXIS)) TILT_OFFSET_LOW_LIMIT")
field(SCAN, "I/O Intr")
}
record(ai, "$(INSTR)$(M):TiltOffsetHighLimitRBV")
{
field(DTYP, "asynFloat64")
field(INP, "@asyn($(CONTROLLER),$(AXIS)) TILT_OFFSET_HIGH_LIMIT")
field(SCAN, "I/O Intr")
}

134
db/detectorTower.db Normal file
View File

@ -0,0 +1,134 @@
# Reset any errors of the virtual axis.
# This record is coupled to the parameter library via resetError_ -> RESET_ERROR.
record(longout, "$(INSTR)$(M):ResetError") {
field(DTYP, "asynInt32")
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) RESET_ERROR")
field(PINI, "NO")
}
# Start the movement into the position defined by BeamAngle, LiftOffset and TiltOffset
record(longout, "$(INSTR)$(M):MoveToWorkingPosition") {
field(DTYP, "asynInt32")
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) MOVE_TO_WORKING_POSITION")
field(PINI, "NO")
}
# Read the position state of the axis:
# 0 = not ready
# 1 = ready (in working position)
# 2 = Moving from working to changer position or in changer position
# 2 = Moving from changer to working position
record(longout, "$(INSTR)$(M):PositionState")
{
field(DTYP, "asynInt32")
field(OUT, "@asyn($(CONTROLLER),$(AXIS)) POSITION_STATE")
field(SCAN, "I/O Intr")
}
# Start the movement into the position defined by BeamAngle, LiftOffset and TiltOffset
record(longout, "$(INSTR)$(M):Start") {
field(DTYP, "asynInt32")
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) START")
field(PINI, "NO")
}
# Set the target angle for the beam
record(ao, "$(INSTR)$(M):BeamAngle") {
field(DTYP, "asynFloat64")
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) BEAM_ANGLE")
field(PINI, "NO")
}
# Copy the high and low limits from the motor record
record(ao, "$(INSTR)$(M):DHLM2BeamAngle_RBV") {
field(DOL, "$(INSTR)$(M).DHLM CP")
field(OUT, "$(INSTR)$(M):BeamAngle.DRVH")
field(OMSL, "closed_loop")
}
record(ao, "$(INSTR)$(M):DLLM2BeamAngle_RBV") {
field(DOL, "$(INSTR)$(M).DLLM CP")
field(OUT, "$(INSTR)$(M):BeamAngle.DRVL")
field(OMSL, "closed_loop")
}
# Set the target position for the offset of the lift (motor COZ)
# This record is coupled to the parameter library via liftOffset_ -> LIFT_OFFSET.
record(ao, "$(INSTR)$(M):LiftOffset") {
field(DTYP, "asynFloat64")
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) LIFT_OFFSET")
field(PINI, "NO")
field(FLNK, "$(INSTR)$(M):LiftOffsetFwd")
}
# Like $(INSTR)$(M):LiftOffset, but starts a movement immediately, if able
record(ao, "$(INSTR)$(M):LiftOffsetMove") {
field(DTYP, "asynFloat64")
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) LIFT_OFFSET_MOVE")
field(PINI, "NO")
}
# Read the lower and upper limits for the lift offset
record(ai, "$(INSTR)$(M):LiftOffsetLowLimitRBV")
{
field(DTYP, "asynFloat64")
field(INP, "@asyn($(CONTROLLER),$(AXIS)) LIFT_OFFSET_LOW_LIMIT")
field(SCAN, "I/O Intr")
}
record(ao, "$(INSTR)$(M):LiftOffsetLowLimit2Field") {
field(DOL, "$(INSTR)$(M):LiftOffsetLowLimitRBV CP")
field(OUT, "$(INSTR)$(M):LiftOffset.DRVL")
field(OMSL, "closed_loop")
}
record(ai, "$(INSTR)$(M):LiftOffsetHighLimitRBV")
{
field(DTYP, "asynFloat64")
field(INP, "@asyn($(CONTROLLER),$(AXIS)) LIFT_OFFSET_HIGH_LIMIT")
field(SCAN, "I/O Intr")
}
record(ao, "$(INSTR)$(M):LiftOffsetHighLimit2Field") {
field(DOL, "$(INSTR)$(M):LiftOffsetHighLimitRBV CP")
field(OUT, "$(INSTR)$(M):LiftOffset.DRVH")
field(OMSL, "closed_loop")
}
# Set the target position for the offset of the detector tilt (motor COX)
# This record is coupled to the parameter library via tiltOffset_ -> TILT_OFFSET.
record(ao, "$(INSTR)$(M):TiltOffset") {
field(DTYP, "asynFloat64")
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) TILT_OFFSET")
field(PINI, "NO")
}
# Like $(INSTR)$(M):TiltOffset, but starts a movement immediately, if able
record(ao, "$(INSTR)$(M):TiltOffsetMove") {
field(DTYP, "asynFloat64")
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) TILT_OFFSET")
field(PINI, "NO")
field(FLNK, "$(INSTR)$(M):Start")
}
# Read the lower and upper limits for the detector tilt offset
record(ai, "$(INSTR)$(M):TiltOffsetLowLimitRBV")
{
field(DTYP, "asynFloat64")
field(INP, "@asyn($(CONTROLLER),$(AXIS)) TILT_OFFSET_LOW_LIMIT")
field(SCAN, "I/O Intr")
}
record(ao, "$(INSTR)$(M):TiltOffsetLowLimit2Field") {
field(DOL, "$(INSTR)$(M):TiltOffsetLowLimitRBV CP")
field(OUT, "$(INSTR)$(M):TiltOffset.DRVL")
field(OMSL, "closed_loop")
}
record(ai, "$(INSTR)$(M):TiltOffsetHighLimitRBV")
{
field(DTYP, "asynFloat64")
field(INP, "@asyn($(CONTROLLER),$(AXIS)) TILT_OFFSET_HIGH_LIMIT")
field(SCAN, "I/O Intr")
}
record(ao, "$(INSTR)$(M):TiltOffsetHighLimit2Field") {
field(DOL, "$(INSTR)$(M):TiltOffsetHighLimitRBV CP")
field(OUT, "$(INSTR)$(M):TiltOffset.DRVH")
field(OMSL, "closed_loop")
}

View File

@ -1,13 +1,35 @@
#include "detectorTowerAxis.h" #include "detectorTowerAxis.h"
#include "detectorTowerController.h"
#include "turboPmacController.h" #include "turboPmacController.h"
#include <epicsExport.h> #include <epicsExport.h>
#include <errlog.h> #include <errlog.h>
#include <iocsh.h> #include <iocsh.h>
detectorTowerAxis::detectorTowerAxis(detectorTowerController *pC, int axisNo) /*
: sinqAxis(pC, axisNo), pC_(pC) { Contains all instances of turboPmacAxis which have been created and is used in
the initialization hook function.
*/
static std::vector<detectorTowerAxis *> axes;
asynStatus status = asynSuccess; /**
* @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<detectorTowerAxis *>::iterator itA = axes.begin();
itA != axes.end(); ++itA) {
detectorTowerAxis *axis = *itA;
axis->init();
}
}
}
detectorTowerAxis::detectorTowerAxis(detectorTowerController *pC, int axisNo)
: turboPmacAxis(pC, axisNo, false), pC_(pC) {
/* /*
The superclass constructor sinqAxis calls in turn its superclass constructor The superclass constructor sinqAxis calls in turn its superclass constructor
@ -37,7 +59,75 @@ detectorTowerAxis::detectorTowerAxis(detectorTowerController *pC, int axisNo)
error_ = 0; error_ = 0;
scheduleMoveFromParamLib_ = false; scheduleMoveFromParamLib_ = false;
// Speed cannot be changed // 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);
}
detectorTowerAxis::~detectorTowerAxis(void) {
// Since the controller memory is managed somewhere else, we don't need to
// clean up the pointer pC here.
}
asynStatus detectorTowerAxis::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_->pasynUserSelf, 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 some values in the parameter library
status = pC_->setDoubleParam(axisNo_, pC_->liftOffset_, 0.0);
if (status != asynSuccess) {
asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nFATAL ERROR "
"(setting a parameter value failed with %s)\n. Terminating IOC",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->stringifyAsynStatus(status));
exit(-1);
}
status = pC_->setDoubleParam(axisNo_, pC_->tiltOffset_, 0.0);
if (status != asynSuccess) {
asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nFATAL ERROR "
"(setting a parameter value failed with %s)\n. Terminating IOC",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->stringifyAsynStatus(status));
exit(-1);
}
// Initialize some values in the parameter library
status = pC_->setIntegerParam(axisNo_, pC_->motorCanSetSpeed_, 0); status = pC_->setIntegerParam(axisNo_, pC_->motorCanSetSpeed_, 0);
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint( asynPrint(
@ -49,16 +139,7 @@ detectorTowerAxis::detectorTowerAxis(detectorTowerController *pC, int axisNo)
exit(-1); exit(-1);
} }
status = pC_->setIntegerParam(axisNo_, pC_->positionState_, 1); return status;
if (status != asynSuccess) {
asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nFATAL ERROR "
"(setting a parameter value failed with %s)\n. Terminating IOC",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->stringifyAsynStatus(status));
exit(-1);
}
} }
// Perform the actual poll // Perform the actual poll
@ -131,8 +212,8 @@ asynStatus detectorTowerAxis::doPoll(bool *moving) {
} }
nvals = nvals =
sscanf(response, "%d %d %lf %d %lf %lf %lf %lf %lf %lf", &inPosition, sscanf(response, "%d %d %d %lf %lf %lf %lf %lf %lf %lf", &inPosition,
&positionState, &beamTiltAngle, &error, &highLimit, &lowLimit, &positionState, &error, &beamTiltAngle, &highLimit, &lowLimit,
&liftOffsetHighLimit, &liftOffsetLowLimit, &tiltOffsetHighLimit, &liftOffsetHighLimit, &liftOffsetLowLimit, &tiltOffsetHighLimit,
&tiltOffsetLowLimit); &tiltOffsetLowLimit);
if (nvals != 10) { if (nvals != 10) {
@ -179,37 +260,49 @@ asynStatus detectorTowerAxis::doPoll(bool *moving) {
*moving = true; *moving = true;
} }
// Intepret the position state // Create the unique callsite identifier manually so it can be used later in
// the shouldBePrinted calls.
msgPrintControlKey keyPosState = msgPrintControlKey(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
// Reset the count, if the status is not an error state
bool resetCountPosState = true;
// Interpret the position state
switch (positionState) { switch (positionState) {
case 0: case 0:
asynPrint( // Axis not ready
pC_->pasynUserSelf, ASYN_TRACE_FLOW,
"Controller \"%s\", axis %d => %s, line %d\nAxis not ready\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
break; break;
case 1: case 1:
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW, // Axis ready
"Controller \"%s\", axis %d => %s, line %d\nAxis ready\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
break; break;
case 2: case 2:
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW, if (pC_->msgPrintControl_.shouldBePrinted(keyPosState, true,
"Controller \"%s\", axis %d => %s, line %d\nAxis in change " pC_->pasynUserSelf)) {
"position\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_FLOW,
"Controller \"%s\", axis %d => %s, line %d\nAxis in change "
"position.%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->msgPrintControl_.getSuffix());
}
resetCountPosState = false;
break; break;
default: default:
*moving = false; *moving = false;
snprintf(userMessage, sizeof(userMessage),
"No air cushion feedback before movement start (P%2.2d01 = "
"%d). Please call the support.",
axisNo_, error);
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, if (pC_->msgPrintControl_.shouldBePrinted(keyPosState, true,
"Controller \"%s\", axis %d => %s, line %d\nReached unknown " pC_->pasynUserSelf)) {
"state P354 = %d\n", asynPrint(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, pC_->pasynUserSelf, ASYN_TRACE_ERROR,
positionState); "Controller \"%s\", axis %d => %s, line %d\nReached unknown "
"state P354 = %d.%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
positionState, pC_->msgPrintControl_.getSuffix());
}
resetCountPosState = false;
snprintf(userMessage, sizeof(userMessage), snprintf(userMessage, sizeof(userMessage),
"Unknown state P354 = %d has been reached. Please call " "Unknown state P354 = %d has been reached. Please call "
"the support.", "the support.",
@ -222,6 +315,10 @@ asynStatus detectorTowerAxis::doPoll(bool *moving) {
} }
} }
if (resetCountPosState) {
pC_->msgPrintControl_.resetCount(keyPosState);
}
if (*moving) { if (*moving) {
// If the axis is moving, evaluate the movement direction // If the axis is moving, evaluate the movement direction
if ((beamTiltAngle - previousBeamTiltAngle) > 0) { if ((beamTiltAngle - previousBeamTiltAngle) > 0) {
@ -231,16 +328,28 @@ asynStatus detectorTowerAxis::doPoll(bool *moving) {
} }
} }
// Create the unique callsite identifier manually so it can be used later in
// the shouldBePrinted calls.
msgPrintControlKey keyError = msgPrintControlKey(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
bool resetError = true;
// Error handling // Error handling
switch (error) { switch (error) {
case 0: case 0:
// No error // No error
break; break;
case 1: case 1:
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
"Controller \"%s\", axis %d => %s, line %d\nBrake COZ not " pC_->pasynUserSelf)) {
"released (P359=1).\n", asynPrint(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, error); pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nBrake COZ not "
"released (P359=1).%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->msgPrintControl_.getSuffix());
}
resetError = false;
pl_status = pl_status =
setStringParam(pC_->motorMessageText_, setStringParam(pC_->motorMessageText_,
@ -256,10 +365,16 @@ asynStatus detectorTowerAxis::doPoll(bool *moving) {
break; break;
case 2: case 2:
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
"Controller \"%s\", axis %d => %s, line %d\nMove command " pC_->pasynUserSelf)) {
"rejected because axis is already moving.\n", asynPrint(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nMove command "
"rejected because axis is already moving.%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->msgPrintControl_.getSuffix());
}
resetError = false;
pl_status = setStringParam(pC_->motorMessageText_, pl_status = setStringParam(pC_->motorMessageText_,
"Move command rejected because axis is " "Move command rejected because axis is "
@ -274,10 +389,15 @@ asynStatus detectorTowerAxis::doPoll(bool *moving) {
break; break;
case 3: case 3:
asynPrint( if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
pC_->pasynUserSelf, ASYN_TRACE_ERROR, pC_->pasynUserSelf)) {
"Controller \"%s\", axis %d => %s, line %d\nError motor FTZ.\n", asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); "Controller \"%s\", axis %d => %s, line %d\nError motor "
"FTZ.%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->msgPrintControl_.getSuffix());
}
resetError = false;
pl_status = pl_status =
setStringParam(pC_->motorMessageText_, setStringParam(pC_->motorMessageText_,
@ -293,10 +413,16 @@ asynStatus detectorTowerAxis::doPoll(bool *moving) {
break; break;
case 4: case 4:
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
"Controller \"%s\", axis %d => %s, line %d\nAxis stopped " pC_->pasynUserSelf)) {
"unexpectedly.\n", asynPrint(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nAxis stopped "
"unexpectedly.%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->msgPrintControl_.getSuffix());
}
resetError = false;
pl_status = pl_status =
setStringParam(pC_->motorMessageText_, setStringParam(pC_->motorMessageText_,
@ -313,10 +439,16 @@ asynStatus detectorTowerAxis::doPoll(bool *moving) {
break; break;
case 5: case 5:
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
"Controller \"%s\", axis %d => %s, line %d\nRelease removed " pC_->pasynUserSelf)) {
"while moving.\n", asynPrint(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nRelease removed "
"while moving.%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->msgPrintControl_.getSuffix());
}
resetError = false;
pl_status = setStringParam( pl_status = setStringParam(
pC_->motorMessageText_, pC_->motorMessageText_,
@ -333,10 +465,16 @@ asynStatus detectorTowerAxis::doPoll(bool *moving) {
break; break;
case 6: case 6:
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
"Controller \"%s\", axis %d => %s, line %d\nEmergency stop " pC_->pasynUserSelf)) {
"activated.\n", asynPrint(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nEmergency stop "
"activated.%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->msgPrintControl_.getSuffix());
}
resetError = false;
pl_status = pl_status =
setStringParam(pC_->motorMessageText_, "Emergency stop activate"); setStringParam(pC_->motorMessageText_, "Emergency stop activate");
@ -350,10 +488,15 @@ asynStatus detectorTowerAxis::doPoll(bool *moving) {
break; break;
case 7: case 7:
asynPrint( if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
pC_->pasynUserSelf, ASYN_TRACE_ERROR, pC_->pasynUserSelf)) {
"Controller \"%s\", axis %d => %s, line %d\nError motor COZ.\n", asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); "Controller \"%s\", axis %d => %s, line %d\nError motor "
"COZ.%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->msgPrintControl_.getSuffix());
}
resetError = false;
pl_status = pl_status =
setStringParam(pC_->motorMessageText_, setStringParam(pC_->motorMessageText_,
@ -369,10 +512,15 @@ asynStatus detectorTowerAxis::doPoll(bool *moving) {
break; break;
case 8: case 8:
asynPrint( if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
pC_->pasynUserSelf, ASYN_TRACE_ERROR, pC_->pasynUserSelf)) {
"Controller \"%s\", axis %d => %s, line %d\nError motor COM.\n", asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); "Controller \"%s\", axis %d => %s, line %d\nError motor "
"COM.%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->msgPrintControl_.getSuffix());
}
resetError = false;
pl_status = pl_status =
setStringParam(pC_->motorMessageText_, setStringParam(pC_->motorMessageText_,
@ -388,10 +536,15 @@ asynStatus detectorTowerAxis::doPoll(bool *moving) {
break; break;
case 9: case 9:
asynPrint( if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
pC_->pasynUserSelf, ASYN_TRACE_ERROR, pC_->pasynUserSelf)) {
"Controller \"%s\", axis %d => %s, line %d\nError motor COX.\n", asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); "Controller \"%s\", axis %d => %s, line %d\nError motor "
"COX.%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->msgPrintControl_.getSuffix());
}
resetError = false;
pl_status = pl_status =
setStringParam(pC_->motorMessageText_, setStringParam(pC_->motorMessageText_,
@ -407,10 +560,15 @@ asynStatus detectorTowerAxis::doPoll(bool *moving) {
break; break;
case 10: case 10:
asynPrint( if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
pC_->pasynUserSelf, ASYN_TRACE_ERROR, pC_->pasynUserSelf)) {
"Controller \"%s\", axis %d => %s, line %d\nHit end switch FTZ.\n", asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); "Controller \"%s\", axis %d => %s, line %d\nHit end "
"switch FTZ.%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->msgPrintControl_.getSuffix());
}
resetError = false;
pl_status = pl_status =
setStringParam(pC_->motorMessageText_, "Hit end switch FTZ"); setStringParam(pC_->motorMessageText_, "Hit end switch FTZ");
@ -425,16 +583,22 @@ asynStatus detectorTowerAxis::doPoll(bool *moving) {
case 11: case 11:
// Following error // Following error
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
"Controller \"%s\", axis %d => %s, line %d\nMaximum allowed " pC_->pasynUserSelf)) {
"following error FTZ exceeded.\n", asynPrint(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nMaximum allowed "
"following error FTZ exceeded.%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->msgPrintControl_.getSuffix());
}
resetError = false;
snprintf(userMessage, sizeof(userMessage), snprintf(userMessage, sizeof(userMessage),
"Maximum allowed following error exceeded (P359 = %d). " "Maximum allowed following error exceeded (P359 = %d). "
"Check if movement range is blocked. " "Check if movement range is blocked. "
"Otherwise please call the support.", "Otherwise please call the support.",
axisNo_, error); error);
pl_status = setStringParam(pC_->motorMessageText_, userMessage); pl_status = setStringParam(pC_->motorMessageText_, userMessage);
if (pl_status != asynSuccess) { if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
@ -446,10 +610,16 @@ asynStatus detectorTowerAxis::doPoll(bool *moving) {
break; break;
default: default:
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
"Controller \"%s\", axis %d => %s, line %d\nUnknown error " pC_->pasynUserSelf)) {
"P359 = %d.\n", asynPrint(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, error); pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nUnknown error "
"P359 = %d.%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, error,
pC_->msgPrintControl_.getSuffix());
}
resetError = false;
snprintf(userMessage, sizeof(userMessage), snprintf(userMessage, sizeof(userMessage),
"Unknown error P359 = %d. Please call the support.", error); "Unknown error P359 = %d. Please call the support.", error);
@ -464,6 +634,10 @@ asynStatus detectorTowerAxis::doPoll(bool *moving) {
break; break;
} }
if (resetError) {
pC_->msgPrintControl_.resetCount(keyError);
}
// Update the parameter library // Update the parameter library
if (error != 0) { if (error != 0) {
pl_status = setIntegerParam(pC_->motorStatusProblem_, true); pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
@ -569,14 +743,49 @@ asynStatus detectorTowerAxis::doPoll(bool *moving) {
// If a movement is scheduled and the axis is not moving anymore, start the // If a movement is scheduled and the axis is not moving anymore, start the
// scheduled movement. This replaces the original motor record movement // scheduled movement. This replaces the original motor record movement
// control loop functionality. // control loop functionality.
if (scheduleMoveFromParamLib_ && !moving && poll_status == asynSuccess) { if (scheduleMoveFromParamLib_ && !(*moving) && poll_status == asynSuccess) {
scheduleMoveFromParamLib_ = false; scheduleMoveFromParamLib_ = false;
// Update the parameter library immediately
pl_status = callParamCallbacks();
if (pC_->msgPrintControl_.shouldBePrinted(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pl_status != asynSuccess, pC_->pasynUserSelf)) {
asynPrint(pC_->pasynUserSelf, 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_->msgPrintControl_.getSuffix());
}
if (pl_status != asynSuccess) {
return poll_status;
}
return moveFromParamLib(); return moveFromParamLib();
} else { } else {
return poll_status; return poll_status;
} }
} }
asynStatus detectorTowerAxis::move(double position, int relative,
double min_velocity, double max_velocity,
double acceleration) {
// Status of parameter library operations
asynStatus pl_status = asynSuccess;
// Set the position as beam angle
pl_status = pC_->setDoubleParam(axisNo_, pC_->beamAngle_, position);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "beamAngle_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
return moveFromParamLib();
}
asynStatus detectorTowerAxis::moveFromParamLib() { asynStatus detectorTowerAxis::moveFromParamLib() {
// Status of read-write-operations of ASCII commands to the controller // Status of read-write-operations of ASCII commands to the controller
@ -586,13 +795,11 @@ asynStatus detectorTowerAxis::moveFromParamLib() {
asynStatus pl_status = asynSuccess; asynStatus pl_status = asynSuccess;
char command[pC_->MAXBUF_], response[pC_->MAXBUF_]; char command[pC_->MAXBUF_], response[pC_->MAXBUF_];
double motorTargetPosition = 0.0;
double motorCoordinatesPosition = 0.0; double motorCoordinatesPosition = 0.0;
double motorRecResolution = 0.0; double motorRecResolution = 0.0;
double beamAngle = 0.0;
double liftOffset = 0.0; double liftOffset = 0.0;
double tiltOffset = 0.0; double tiltOffset = 0.0;
double motorVelocity = 0.0;
int motorCanSetSpeed = 0;
int writeOffset = 0; int writeOffset = 0;
int positionState = 0; int positionState = 0;
@ -607,21 +814,27 @@ asynStatus detectorTowerAxis::moveFromParamLib() {
// If the axis is in changer position, it must be moved into working // If the axis is in changer position, it must be moved into working
// position before any move can be started. // position before any move can be started.
if (positionState == 2) { bool isInChangerPos = positionState == 2 || positionState == 3;
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW, if (pC_->msgPrintControl_.shouldBePrinted(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
isInChangerPos, pC_->pasynUserSelf)) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nAxis cannot be " "Controller \"%s\", axis %d => %s, line %d\nAxis cannot be "
"moved because it is in changer " "moved because it is moving from working to changer "
"position. Move it to working position first.\n", "position, is in changer position or is moving from changer "
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); "to working position.%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->msgPrintControl_.getSuffix());
}
if (isInChangerPos) {
return asynError;
} }
// Read the target values from the parameter library // Read the target values from the parameter library
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorTargetPosition_, pl_status = pC_->getDoubleParam(axisNo_, pC_->beamAngle_, &beamAngle);
&motorTargetPosition);
if (pl_status != asynSuccess) { if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorTargetPosition_", return pC_->paramLibAccessFailed(pl_status, "beamAngle_", axisNo_,
axisNo_, __PRETTY_FUNCTION__, __PRETTY_FUNCTION__, __LINE__);
__LINE__);
} }
pl_status = pC_->getDoubleParam(axisNo_, pC_->liftOffset_, &liftOffset); pl_status = pC_->getDoubleParam(axisNo_, pC_->liftOffset_, &liftOffset);
if (pl_status != asynSuccess) { if (pl_status != asynSuccess) {
@ -633,7 +846,6 @@ asynStatus detectorTowerAxis::moveFromParamLib() {
return pC_->paramLibAccessFailed(pl_status, "tiltOffset_", axisNo_, return pC_->paramLibAccessFailed(pl_status, "tiltOffset_", axisNo_,
__PRETTY_FUNCTION__, __LINE__); __PRETTY_FUNCTION__, __LINE__);
} }
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution_, pl_status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution_,
&motorRecResolution); &motorRecResolution);
if (pl_status != asynSuccess) { if (pl_status != asynSuccess) {
@ -645,13 +857,12 @@ asynStatus detectorTowerAxis::moveFromParamLib() {
// Set the target positions for beam tilt, detector tilt offset and lift // Set the target positions for beam tilt, detector tilt offset and lift
// offset // offset
snprintf(&command[writeOffset], sizeof(command) - writeOffset, snprintf(&command[writeOffset], sizeof(command) - writeOffset,
"Q451=%lf Q454=%lf Qxxx=%lf P350=1", motorTargetPosition, "Q451=%lf Q454=%lf Q456=%lf P350=1", beamAngle, liftOffset,
liftOffset, tiltOffset); tiltOffset);
// We don't expect an answer // We don't expect an answer
rw_status = pC_->writeRead(axisNo_, command, response, 0); rw_status = pC_->writeRead(axisNo_, command, response, 0);
if (rw_status != asynSuccess) { if (rw_status != asynSuccess) {
asynPrint( asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_ERROR, pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nStarting movement to " "Controller \"%s\", axis %d => %s, line %d\nStarting movement to "
@ -665,6 +876,10 @@ asynStatus detectorTowerAxis::moveFromParamLib() {
__LINE__); __LINE__);
} }
} }
// Start polling immediately
pC_->wakeupPoller();
return rw_status; return rw_status;
} }
@ -695,12 +910,13 @@ asynStatus detectorTowerAxis::stop(double acceleration) {
axisNo_, __PRETTY_FUNCTION__, axisNo_, __PRETTY_FUNCTION__,
__LINE__); __LINE__);
} }
return asynError;
} }
return rw_status; return rw_status;
} }
// The AMOR detector axis uses absolute encoders // The detector tower axis uses absolute encoders
asynStatus detectorTowerAxis::readEncoderType() { asynStatus detectorTowerAxis::readEncoderType() {
asynStatus status = setStringParam(pC_->encoderType_, AbsoluteEncoder); asynStatus status = setStringParam(pC_->encoderType_, AbsoluteEncoder);
@ -713,9 +929,7 @@ asynStatus detectorTowerAxis::readEncoderType() {
asynStatus detectorTowerAxis::moveToWorkingPosition(bool toWorkingPosition) { asynStatus detectorTowerAxis::moveToWorkingPosition(bool toWorkingPosition) {
int timeout_enable_disable = 2; char response[pC_->MAXBUF_];
char command[pC_->MAXBUF_], response[pC_->MAXBUF_];
int nvals = 0;
// Status of read-write-operations of ASCII commands to the controller // Status of read-write-operations of ASCII commands to the controller
asynStatus rw_status = asynSuccess; asynStatus rw_status = asynSuccess;
@ -741,12 +955,18 @@ asynStatus detectorTowerAxis::moveToWorkingPosition(bool toWorkingPosition) {
// command and inform the user. We check the last known status of the axis // command and inform the user. We check the last known status of the axis
// instead of "moving", since status -6 is also moving, but the motor can // instead of "moving", since status -6 is also moving, but the motor can
// actually be disabled in this state! // actually be disabled in this state!
if (moving) {
if (pC_->msgPrintControl_.shouldBePrinted(pC_->portName, axisNo_,
__PRETTY_FUNCTION__, __LINE__,
moving, pC_->pasynUserSelf)) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nAxis is not " "Controller \"%s\", axis %d => %s, line %d\nAxis is not "
"idle and can therefore not be moved to changer position.\n", "idle and can therefore not be moved to %s position.%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
toWorkingPosition ? "working" : "changer",
pC_->msgPrintControl_.getSuffix());
}
if (moving) {
pl_status = setStringParam( pl_status = setStringParam(
pC_->motorMessageText_, pC_->motorMessageText_,
"Axis cannot be moved to changer position while it is moving."); "Axis cannot be moved to changer position while it is moving.");
@ -755,19 +975,24 @@ asynStatus detectorTowerAxis::moveToWorkingPosition(bool toWorkingPosition) {
axisNo_, __PRETTY_FUNCTION__, axisNo_, __PRETTY_FUNCTION__,
__LINE__); __LINE__);
} }
return asynError; return asynError;
} }
// Axis is already enabled / disabled and a new enable / disable command // Axis is already in the correct position
// was sent => Do nothing bool isAlreadyThere = (toWorkingPosition == true && positionState == 1) ||
if ((toWorkingPosition == true && positionState == 1) || (toWorkingPosition == false && positionState == 2);
(toWorkingPosition == false && positionState == 2)) {
if (pC_->msgPrintControl_.shouldBePrinted(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
isAlreadyThere, pC_->pasynUserSelf)) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_WARNING, asynPrint(pC_->pasynUserSelf, ASYN_TRACE_WARNING,
"Controller \"%s\", axis %d => %s, line %d\nAxis is already " "Controller \"%s\", axis %d => %s, line %d\nAxis is already "
"in %s position.\n", "in %s position.%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
toWorkingPosition ? "working" : "changer"); toWorkingPosition ? "working" : "changer",
pC_->msgPrintControl_.getSuffix());
}
if (isAlreadyThere) {
return asynSuccess; return asynSuccess;
} }
@ -776,6 +1001,7 @@ asynStatus detectorTowerAxis::moveToWorkingPosition(bool toWorkingPosition) {
rw_status = pC_->writeRead(axisNo_, "P350=3", response, 0); rw_status = pC_->writeRead(axisNo_, "P350=3", response, 0);
pl_status = setStringParam(pC_->motorMessageText_, pl_status = setStringParam(pC_->motorMessageText_,
"Moving to working position ..."); "Moving to working position ...");
} else { } else {
rw_status = pC_->writeRead(axisNo_, "P350=2", response, 0); rw_status = pC_->writeRead(axisNo_, "P350=2", response, 0);
pl_status = setStringParam(pC_->motorMessageText_, pl_status = setStringParam(pC_->motorMessageText_,
@ -786,7 +1012,7 @@ asynStatus detectorTowerAxis::moveToWorkingPosition(bool toWorkingPosition) {
axisNo_, __PRETTY_FUNCTION__, axisNo_, __PRETTY_FUNCTION__,
__LINE__); __LINE__);
} }
return pC_->writeRead(axisNo_, command, response, 0); return rw_status;
} }
asynStatus detectorTowerAxis::reset() { asynStatus detectorTowerAxis::reset() {
@ -810,25 +1036,10 @@ asynStatus detectorTowerAxis::reset() {
- If any other error: P352 = 2 (Reset error) - If any other error: P352 = 2 (Reset error)
*/ */
if (positionState == 0) { if (positionState == 0) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW,
"Controller \"%s\", axis %d => %s, line %d\nSetting axis in "
"closed-loop mode.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
return pC_->writeRead(axisNo_, "P352=1", response, 0); return pC_->writeRead(axisNo_, "P352=1", response, 0);
} else if (error_ == 10 || error_ == 11) { } else if (error_ == 10 || error_ == 11) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW,
"Controller \"%s\", axis %d => %s, line %d\nResetting FTZ "
"motor error.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
return pC_->writeRead(axisNo_, "P352=3", response, 0); return pC_->writeRead(axisNo_, "P352=3", response, 0);
} else if (error_ != 0) { } else if (error_ != 0) {
asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_FLOW,
"Controller \"%s\", axis %d => %s, line %d\nResetting error.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
return pC_->writeRead(axisNo_, "P352=2", response, 0); return pC_->writeRead(axisNo_, "P352=2", response, 0);
} else { } else {
return asynSuccess; return asynSuccess;
@ -879,7 +1090,7 @@ asynStatus detectorTowerCreateAxis(const char *portName, int axis) {
detectorTowerController *pC = dynamic_cast<detectorTowerController *>(apd); detectorTowerController *pC = dynamic_cast<detectorTowerController *>(apd);
if (pC == nullptr) { if (pC == nullptr) {
errlogPrintf("Controller \"%s\" => %s, line %d\nController " errlogPrintf("Controller \"%s\" => %s, line %d\nController "
"is not a turboPmacController.", "is not a detectorTowerController.",
portName, __PRETTY_FUNCTION__, __LINE__); portName, __PRETTY_FUNCTION__, __LINE__);
return asynError; return asynError;
} }
@ -929,4 +1140,4 @@ static void detectorTowerAxisRegister(void) {
} }
epicsExportRegistrar(detectorTowerAxisRegister); epicsExportRegistrar(detectorTowerAxisRegister);
} // extern "C" } // extern "C"

View File

@ -1,17 +1,17 @@
#ifndef detectorTowerAXIS_H #ifndef detectorTowerAxis_H
#define detectorTowerAXIS_H #define detectorTowerAxis_H
#include "detectorTowerController.h" #include "turboPmacAxis.h"
#include "sinqAxis.h" #include <errlog.h>
// Forward declaration of the controller class to resolve the cyclic dependency // Forward declaration of the controller class to resolve the cyclic dependency
// between the controller and the axis .h-file. See // between the controller and the axis .h-file. See
// https://en.cppreference.com/w/cpp/language/class. // https://en.cppreference.com/w/cpp/language/class.
class detectorTowerController; class detectorTowerController;
class detectorTowerAxis : public sinqAxis { class detectorTowerAxis : public turboPmacAxis {
public: public:
/** /**
* @brief Construct a new turboPmacAxis * @brief Construct a new detectorTowerAxis
* *
* @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
@ -69,9 +69,7 @@ class detectorTowerAxis : public sinqAxis {
* @return asynStatus * @return asynStatus
*/ */
asynStatus move(double position, int relative, double min_velocity, asynStatus move(double position, int relative, double min_velocity,
double max_velocity, double acceleration) { double max_velocity, double acceleration);
return asynSuccess;
}
/** /**
* @brief Start a movement based on the parameter library values for * @brief Start a movement based on the parameter library values for
@ -109,10 +107,6 @@ class detectorTowerAxis : public sinqAxis {
detectorTowerController *pC_; detectorTowerController *pC_;
bool scheduleMoveFromParamLib_; bool scheduleMoveFromParamLib_;
bool waitForHandshake_;
time_t timeAtHandshake_;
bool inChangerPosition_;
int error_; int error_;
private: private:

View File

@ -1,6 +1,8 @@
#include "detectorTowerController.h" #include "detectorTowerController.h"
#include "detectorTowerAxis.h"
#include "turboPmacController.h" #include "turboPmacController.h"
#include <epicsExport.h> #include <epicsExport.h>
#include <errlog.h>
#include <iocsh.h> #include <iocsh.h>
/** /**
@ -38,6 +40,17 @@ detectorTowerController::detectorTowerController(
exit(-1); exit(-1);
} }
status =
createParam("LIFT_OFFSET_MOVE", asynParamFloat64, &liftOffsetMove_);
if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d\nFATAL ERROR (creating a "
"parameter failed with %s).\nTerminating IOC",
portName, __PRETTY_FUNCTION__, __LINE__,
stringifyAsynStatus(status));
exit(-1);
}
status = createParam("LIFT_OFFSET_LOW_LIMIT", asynParamFloat64, status = createParam("LIFT_OFFSET_LOW_LIMIT", asynParamFloat64,
&liftOffsetLowLimit_); &liftOffsetLowLimit_);
if (status != asynSuccess) { if (status != asynSuccess) {
@ -70,6 +83,17 @@ detectorTowerController::detectorTowerController(
exit(-1); exit(-1);
} }
status =
createParam("TILT_OFFSET_MOVE", asynParamFloat64, &tiltOffsetMove_);
if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d\nFATAL ERROR (creating a "
"parameter failed with %s).\nTerminating IOC",
portName, __PRETTY_FUNCTION__, __LINE__,
stringifyAsynStatus(status));
exit(-1);
}
status = createParam("TILT_OFFSET_LOW_LIMIT", asynParamFloat64, status = createParam("TILT_OFFSET_LOW_LIMIT", asynParamFloat64,
&tiltOffsetLowLimit_); &tiltOffsetLowLimit_);
if (status != asynSuccess) { if (status != asynSuccess) {
@ -92,6 +116,16 @@ detectorTowerController::detectorTowerController(
exit(-1); exit(-1);
} }
status = createParam("BEAM_ANGLE", asynParamFloat64, &beamAngle_);
if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d\nFATAL ERROR (creating a "
"parameter failed with %s).\nTerminating IOC",
portName, __PRETTY_FUNCTION__, __LINE__,
stringifyAsynStatus(status));
exit(-1);
}
status = createParam("POSITION_STATE", asynParamInt32, &positionState_); status = createParam("POSITION_STATE", asynParamInt32, &positionState_);
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
@ -102,7 +136,28 @@ detectorTowerController::detectorTowerController(
exit(-1); exit(-1);
} }
status = createParam("RESET", asynParamInt32, &reset_); status = createParam("START", asynParamInt32, &start_);
if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d\nFATAL ERROR (creating a "
"parameter failed with %s).\nTerminating IOC",
portName, __PRETTY_FUNCTION__, __LINE__,
stringifyAsynStatus(status));
exit(-1);
}
status = createParam("MOVE_TO_WORKING_POSITION", asynParamInt32,
&moveToWorkingPosition_);
if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d\nFATAL ERROR (creating a "
"parameter failed with %s).\nTerminating IOC",
portName, __PRETTY_FUNCTION__, __LINE__,
stringifyAsynStatus(status));
exit(-1);
}
status = createParam("RESET_ERROR", asynParamInt32, &resetError_);
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d\nFATAL ERROR (creating a " "Controller \"%s\" => %s, line %d\nFATAL ERROR (creating a "
@ -115,12 +170,20 @@ detectorTowerController::detectorTowerController(
asynStatus detectorTowerController::readInt32(asynUser *pasynUser, asynStatus detectorTowerController::readInt32(asynUser *pasynUser,
epicsInt32 *value) { epicsInt32 *value) {
// The detector tower cannot be disabled
if (pasynUser->reason == motorCanDisable_) { // Check if the axis is a detectorTowerAxis
*value = 0; detectorTowerAxis *axis = getDetectorTowerAxis(pasynUser);
return asynSuccess; if (axis == nullptr) {
} else { // This is apparently a "normal" turboPmacAxis
return turboPmacController::readInt32(pasynUser, value); return turboPmacController::readInt32(pasynUser, value);
} else {
// The detector tower cannot be disabled
if (pasynUser->reason == motorCanDisable_) {
*value = 0;
return asynSuccess;
} else {
return turboPmacController::readInt32(pasynUser, value);
}
} }
} }
@ -130,22 +193,40 @@ asynStatus detectorTowerController::writeInt32(asynUser *pasynUser,
// ===================================================================== // =====================================================================
detectorTowerAxis *axis = getVirtualAxis(pasynUser); detectorTowerAxis *axis = getDetectorTowerAxis(pasynUser);
if (axis == nullptr) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nAxis is not an "
"instance of sinqAxis",
portName, axis->axisNo_, __PRETTY_FUNCTION__, __LINE__);
return asynError;
}
// Handle custom PVs if (axis == nullptr) {
if (function == motorEnable_) { // This is apparently a "normal" turboPmacAxis
return axis->enable(value);
} else if (function == reset_) {
return axis->reset();
} else {
return turboPmacController::writeInt32(pasynUser, value); return turboPmacController::writeInt32(pasynUser, value);
} else {
// Handle custom PVs
if (function == start_) {
// Start the movement, if the axis is not already moving.
// Otherwise stop it and schedule a movement.
int done = 0;
asynStatus status =
getIntegerParam(axis->axisNo_, motorStatusDone_, &done);
if (status != asynSuccess) {
return paramLibAccessFailed(status, "motorStatusDone_",
axis->axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
if (done == 1) {
return axis->moveFromParamLib();
} else {
axis->scheduleMoveFromParamLib_ = true;
return axis->stop(0.0);
}
} else if (function == moveToWorkingPosition_) {
return axis->moveToWorkingPosition(value != 0);
} else if (function == resetError_) {
return axis->reset();
} else {
return turboPmacController::writeInt32(pasynUser, value);
}
} }
} }
@ -153,30 +234,33 @@ asynStatus detectorTowerController::writeFloat64(asynUser *pasynUser,
epicsFloat64 value) { epicsFloat64 value) {
int function = pasynUser->reason; int function = pasynUser->reason;
// ===================================================================== detectorTowerAxis *axis = getDetectorTowerAxis(pasynUser);
detectorTowerAxis *axis = getVirtualAxis(pasynUser);
if (axis == nullptr) { if (axis == nullptr) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, // This is apparently a "normal" turboPmacAxis
"Controller \"%s\", axis %d => %s, line %d:\nAxis is not an "
"instance of sinqAxis",
portName, axis->axisNo_, __PRETTY_FUNCTION__, __LINE__);
return asynError;
}
if (function == motorTargetPosition_) {
// Start the movement, if the axis is not already moving. Otherwise stop
// it and schedule a movement.
bool moving = false;
axis->poll(&moving);
if (moving) {
axis->scheduleMoveFromParamLib_ = true;
return axis->stop(0.0);
} else {
return axis->moveFromParamLib();
}
} else {
return turboPmacController::writeFloat64(pasynUser, value); return turboPmacController::writeFloat64(pasynUser, value);
} else {
if (function == liftOffsetMove_) {
asynStatus status =
setDoubleParam(axis->axisNo_, liftOffset_, value);
if (status != asynSuccess) {
return paramLibAccessFailed(status, "liftOffset_",
axis->axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
return axis->moveFromParamLib();
} else if (function == tiltOffsetMove_) {
asynStatus status =
setDoubleParam(axis->axisNo_, tiltOffset_, value);
if (status != asynSuccess) {
return paramLibAccessFailed(status, "tiltOffset_",
axis->axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
return axis->moveFromParamLib();
} else {
return turboPmacController::writeFloat64(pasynUser, value);
}
} }
} }
@ -186,40 +270,18 @@ If the axis does not exist or is not a Axis, a nullptr is returned and an
error is emitted. error is emitted.
*/ */
detectorTowerAxis * detectorTowerAxis *
detectorTowerController::getVirtualAxis(asynUser *pasynUser) { detectorTowerController::getDetectorTowerAxis(asynUser *pasynUser) {
asynMotorAxis *asynAxis = asynMotorController::getAxis(pasynUser); asynMotorAxis *asynAxis = asynMotorController::getAxis(pasynUser);
return detectorTowerController::castToVirtualAxis(asynAxis); return dynamic_cast<detectorTowerAxis *>(asynAxis);
} }
/* /*
Access one of the axes of the controller via the axis index. Access one of the axes of the controller via the axis index.
If the axis does not exist or is not a Axis, the function must return Null If the axis does not exist or is not a Axis, the function must return Null
*/ */
detectorTowerAxis *detectorTowerController::getVirtualAxis(int axisNo) { detectorTowerAxis *detectorTowerController::getDetectorTowerAxis(int axisNo) {
asynMotorAxis *asynAxis = asynMotorController::getAxis(axisNo); asynMotorAxis *asynAxis = asynMotorController::getAxis(axisNo);
return detectorTowerController::castToVirtualAxis(asynAxis); return dynamic_cast<detectorTowerAxis *>(asynAxis);
}
detectorTowerAxis *
detectorTowerController::castToVirtualAxis(asynMotorAxis *asynAxis) {
// =========================================================================
// If the axis slot of the pAxes_ array is empty, a nullptr must be returned
if (asynAxis == nullptr) {
return nullptr;
}
// Here, an error is emitted since asyn_axis is not a nullptr but also not
// an instance of Axis
detectorTowerAxis *axis = dynamic_cast<detectorTowerAxis *>(asynAxis);
if (axis == nullptr) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nAxis is not "
"an instance of turboPmacAxis",
portName, axis->axisNo_, __PRETTY_FUNCTION__, __LINE__);
}
return axis;
} }
/*************************************************************************************/ /*************************************************************************************/
@ -292,4 +354,4 @@ static void detectorTowerControllerRegister(void) {
} }
epicsExportRegistrar(detectorTowerControllerRegister); epicsExportRegistrar(detectorTowerControllerRegister);
} // extern "C" } // extern "C"

View File

@ -33,20 +33,38 @@ class detectorTowerController : public turboPmacController {
int numAxes, double movingPollPeriod, int numAxes, double movingPollPeriod,
double idlePollPeriod, double comTimeout); double idlePollPeriod, double comTimeout);
/**
* @brief Overloaded function of turboPmacController
*
* The function is overloaded in order to read motorCanDisable_
*
* @param pasynUser
* @param value
* @return asynStatus
*/
asynStatus readInt32(asynUser *pasynUser, epicsInt32 *value); asynStatus readInt32(asynUser *pasynUser, epicsInt32 *value);
asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value);
/** /**
* @brief Overloaded function of asynMotorController * @brief Overloaded function of turboPmacController
* *
* The function is overloaded to allow manual starting of an axis movement * The function is overloaded to allow resetting an error.
* *
* @param pasynUser Specify the axis via the asynUser * @param pasynUser Specify the axis via the asynUser
* @param value New value * @param value New value
* @return asynStatus * @return asynStatus
*/ */
virtual asynStatus writeFloat64(asynUser *pasynUser, epicsFloat64 value); asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value);
/**
* @brief Overloaded function of turboPmacController
*
* The function is overloaded to immediate start of an offset movement
*
* @param pasynUser Specify the axis via the asynUser
* @param value New value
* @return asynStatus
*/
asynStatus writeFloat64(asynUser *pasynUser, epicsFloat64 value);
/** /**
* @brief Get the axis object * @brief Get the axis object
@ -55,7 +73,7 @@ class detectorTowerController : public turboPmacController {
* @return detectorTowerAxis* If no axis could be found, this is a * @return detectorTowerAxis* If no axis could be found, this is a
* nullptr * nullptr
*/ */
detectorTowerAxis *getVirtualAxis(asynUser *pasynUser); detectorTowerAxis *getDetectorTowerAxis(asynUser *pasynUser);
/** /**
* @brief Get the axis object * @brief Get the axis object
@ -64,29 +82,25 @@ class detectorTowerController : public turboPmacController {
* @return detectorTowerAxis* If no axis could be found, this is a * @return detectorTowerAxis* If no axis could be found, this is a
* nullptr * nullptr
*/ */
detectorTowerAxis *getVirtualAxis(int axisNo); detectorTowerAxis *getDetectorTowerAxis(int axisNo);
/**
* @brief Save cast of the given asynAxis pointer to a detectorTowerAxis
* pointer. If the cast fails, this function returns a nullptr.
*
* @param asynAxis
* @return detectorTowerAxis*
*/
detectorTowerAxis *castToVirtualAxis(asynMotorAxis *asynAxis);
private: private:
// Indices of additional PVs // Indices of additional PVs
#define FIRST_detectorTower_PARAM liftOffset_ #define FIRST_detectorTower_PARAM liftOffset_
int liftOffset_; int liftOffset_;
int liftOffsetMove_;
int liftOffsetLowLimit_; int liftOffsetLowLimit_;
int liftOffsetHighLimit_; int liftOffsetHighLimit_;
int tiltOffset_; int tiltOffset_;
int tiltOffsetMove_;
int tiltOffsetLowLimit_; int tiltOffsetLowLimit_;
int tiltOffsetHighLimit_; int tiltOffsetHighLimit_;
int beamAngle_;
int positionState_; int positionState_;
int reset_; int start_;
#define LAST_detectorTower_PARAM reset_ int moveToWorkingPosition_;
int resetError_;
#define LAST_detectorTower_PARAM resetError_
friend class detectorTowerAxis; friend class detectorTowerAxis;
}; };