diff --git a/Makefile b/Makefile index 013243f..0b5cd36 100644 --- a/Makefile +++ b/Makefile @@ -13,18 +13,18 @@ REQUIRED+=motorBase motorBase_VERSION=7.2.2 # Specify the version of sinqMotor we want to build against -sinqMotor_VERSION=0.8.0 +sinqMotor_VERSION=mathis_s # Specify the version of turboPmac we want to build against -turboPmac_VERSION=0.7.0 +turboPmac_VERSION=mathis_s # These headers allow to depend on this library for derived drivers. -HEADERS += src/offsetAxis.h +HEADERS += src/auxiliaryAxis.h HEADERS += src/detectorTowerAxis.h HEADERS += src/detectorTowerController.h # Source files to build -SOURCES += src/offsetAxis.cpp +SOURCES += src/auxiliaryAxis.cpp SOURCES += src/detectorTowerAxis.cpp SOURCES += src/detectorTowerController.cpp diff --git a/README.md b/README.md index a3f560c..9be1ae5 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,16 @@ # detectorTower -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: +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. - `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`. +- `auxiliaryAxis`: This is an auxiliary axis type attached to the main detector axis. Multiple instances of these axes are constructed automatically when creating a `detectorTowerAxis`. 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. +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 `auxiliaryAxis`, which are used to perform secondary movements. -## Developer guide +The utilities provided in the `utils` folder of https://git.psi.ch/sinq-epics-modules/turboPmac work with this driver as well. ### Usage in IOC shell @@ -17,7 +18,15 @@ 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: +The constructor function for `detectorTowerAxis` has the following syntax: + +``` +detectorTowerAxis("$(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). + +"Normal" `turboPmacAxis` may be used together with this controller: ``` # Define the name of the controller and the corresponding port @@ -35,10 +44,10 @@ drvAsynIPPortConfigure("$(ASYN_PORT)","172.28.101.24:1025") # 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); +# 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); +turboPmacAxis("$(NAME)",4); +turboPmacAxis("$(NAME)",5); # Set the number of subsequent timeouts setMaxSubsequentTimeouts("$(NAME)", 20); @@ -57,6 +66,49 @@ dbLoadTemplate("$(TOP)/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLE 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 parametrizatioin in the substitution file. If additional axes are used in the axis, they are parametrized as usual: + +detectorTowerAxis("$(NAME)",1, 2, 3); +turboPmacAxis("$(NAME)",4); +turboPmacAxis("$(NAME)",5); + +``` +file "$(SINQDBPATH)" +{ +pattern +{ AXIS, M, DESC, EGU, DIR, MRES, MSGTEXTSIZE, ENABLEMOVWATCHDOG, LIMITSOFFSET, CANSETSPEED } +{ 1, "tower", "Angle of the beam guide", degree, Pos, 0.001, 200, 0, 1.0, 0 } +{ 2, "liftZeroCorr", "Detector vertical lift offset", mm, Pos, 0.001, 200, 0, 1.0, 0 } +{ 3, "tiltZeroCorr", "Detector tilt offset", degree, 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 + +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 `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 `auxiliaryAxis` checks if its associated `detectorTowerAxis` 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`. + +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: + +``` +caput tower 2 & caput liftZeroCorr 10 +``` + +When using the axis from NICOS, using the `maw` or `move` command with multiple devices has the same effect: + +``` +move("tower", 2, "liftZeroCorr", 10) +``` + ### Versioning Please see the documentation for the module sinqMotor: https://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/main/README.md. diff --git a/db/detectorTower.db b/db/detectorTower.db index 0a98bb2..7d878df 100644 --- a/db/detectorTower.db +++ b/db/detectorTower.db @@ -1,26 +1,38 @@ -# Reset any errors of the virtual axis. -# This record is coupled to the parameter library via resetError_ -> RESET_ERROR. -record(longout, "$(INSTR)$(M):ResetError") { +# 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) RESET_ERROR") - field(PINI, "NO") -} - -# Start the movement into the position defined by DetectorTower, LiftOffset and TiltOffset -record(longout, "$(INSTR)$(M):MoveToWorkingPosition") { - field(DTYP, "asynInt32") - field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) MOVE_TO_WORKING_POSITION") + field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) CHANGE_STATE") field(PINI, "NO") + field(DRVH, "1") + field(DRVL, "0") } # 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") +# 3 = Moving from changer to working state +record(longin, "$(INSTR)$(M):PositionStateRBV") { field(DTYP, "asynInt32") - field(OUT, "@asyn($(CONTROLLER),$(AXIS)) POSITION_STATE") + field(INP, "@asyn($(CONTROLLER),$(AXIS)) POSITION_STATE_RBV") field(SCAN, "I/O Intr") -} \ No newline at end of file + field(PINI, "NO") +} + +# This PV combines the position state and the movement readback value from the +# motor record: +# - 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 +record(calc, "$(INSTR)$(M):ChangingStateRBV") +{ + field(INPA, "$(INSTR)$(M).MOVN CP") + field(INPB, "$(INSTR)$(M):PositionStateRBV CP") + field(CALC, "(B == 1 || (B == 2 && A == 1)) ? 0 : 1") +} + +# Convert the double value from the calc record to an integer value +record(longout, "$(INSTR)$(M):ChangingStateRBV_int") { + field(DOL, "$(INSTR)$(M):ChangingStateRBV CP") + field(OMSL, "closed_loop") +} diff --git a/src/offsetAxis.cpp b/src/auxiliaryAxis.cpp similarity index 82% rename from src/offsetAxis.cpp rename to src/auxiliaryAxis.cpp index fc89afe..f5f4fb6 100644 --- a/src/offsetAxis.cpp +++ b/src/auxiliaryAxis.cpp @@ -1,4 +1,4 @@ -#include "offsetAxis.h" +#include "auxiliaryAxis.h" #include "detectorTowerAxis.h" #include "detectorTowerController.h" #include "turboPmacController.h" @@ -7,10 +7,10 @@ #include /* -Contains all instances of offsetAxis which have been created and is used +Contains all instances of auxiliaryAxis which have been created and is used in the initialization hook function. */ -static std::vector axes; +static std::vector axes; /** * @brief Hook function to perform certain actions during the IOC initialization @@ -21,15 +21,15 @@ 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::iterator itA = axes.begin(); + for (std::vector::iterator itA = axes.begin(); itA != axes.end(); ++itA) { - offsetAxis *axis = *itA; + auxiliaryAxis *axis = *itA; axis->init(); } } } -offsetAxis::offsetAxis(detectorTowerController *pC, int axisNo) +auxiliaryAxis::auxiliaryAxis(detectorTowerController *pC, int axisNo) : turboPmacAxis(pC, axisNo, false), pC_(pC) { /* @@ -69,12 +69,12 @@ offsetAxis::offsetAxis(detectorTowerController *pC, int axisNo) axes.push_back(this); } -offsetAxis::~offsetAxis(void) { +auxiliaryAxis::~auxiliaryAxis(void) { // Since the controller memory is managed somewhere else, we don't need to // clean up the pointer pC here. } -asynStatus offsetAxis::init() { +asynStatus auxiliaryAxis::init() { // Local variable declaration asynStatus status = asynSuccess; @@ -108,7 +108,7 @@ asynStatus offsetAxis::init() { } // Perform the actual poll -asynStatus offsetAxis::doPoll(bool *moving) { +asynStatus auxiliaryAxis::doPoll(bool *moving) { // Return value for the poll asynStatus poll_status = asynSuccess; @@ -164,14 +164,32 @@ asynStatus offsetAxis::doPoll(bool *moving) { __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_->msgPrintControl_.shouldBePrinted( + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, wantToPrint, + 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 (wantToPrint) { + poll_status = pl_status; + } + // The limits are written into this class instance inside the doPoll // function of detectorTowerAxis return poll_status; } -asynStatus offsetAxis::doMove(double position, int relative, - double min_velocity, double max_velocity, - double acceleration) { +asynStatus auxiliaryAxis::doMove(double position, int relative, + double min_velocity, double max_velocity, + double acceleration) { double motorRecResolution = 0.0; asynStatus pl_status = pC_->getDoubleParam( @@ -190,7 +208,7 @@ asynStatus offsetAxis::doMove(double position, int relative, return asynSuccess; } -asynStatus offsetAxis::stop(double acceleration) { +asynStatus auxiliaryAxis::stop(double acceleration) { // Status of read-write-operations of ASCII commands to the controller asynStatus rw_status = asynSuccess; diff --git a/src/offsetAxis.h b/src/auxiliaryAxis.h similarity index 90% rename from src/offsetAxis.h rename to src/auxiliaryAxis.h index 16a210e..ba19139 100644 --- a/src/offsetAxis.h +++ b/src/auxiliaryAxis.h @@ -1,5 +1,5 @@ -#ifndef offsetAxis_H -#define offsetAxis_H +#ifndef auxiliaryAxis_H +#define auxiliaryAxis_H #include "turboPmacAxis.h" // Forward declaration of the controller class to resolve the cyclic dependency @@ -8,7 +8,7 @@ class detectorTowerController; class detectorTowerAxis; -class offsetAxis : public turboPmacAxis { +class auxiliaryAxis : public turboPmacAxis { public: /** * @brief Construct a new detectorTowerAxis @@ -16,13 +16,13 @@ class offsetAxis : public turboPmacAxis { * @param pController Pointer to the associated controller * @param axisNo Index of the axis */ - offsetAxis(detectorTowerController *pController, int axisNo); + auxiliaryAxis(detectorTowerController *pController, int axisNo); /** * @brief Destroy the turboPmacAxis * */ - virtual ~offsetAxis(); + virtual ~auxiliaryAxis(); /** * @brief Readout of some values from the controller at IOC startup diff --git a/src/detectorTowerAxis.cpp b/src/detectorTowerAxis.cpp index 0e73e49..124c6ec 100644 --- a/src/detectorTowerAxis.cpp +++ b/src/detectorTowerAxis.cpp @@ -33,20 +33,23 @@ static void deferredMovementCollectorLoop(void *drvPvt) { detectorTowerAxis *axis = (detectorTowerAxis *)drvPvt; while (1) { if (axis->receivedTarget_) { - // Wait for 50 ms and then start the movement with the information + // Wait for 100 ms and then start the movement with the information // available - epicsThreadSleep(0.05); + axis->startingDeferredMovement_ = true; + epicsThreadSleep(0.1); 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); } } detectorTowerAxis::detectorTowerAxis(detectorTowerController *pC, int axisNo, - offsetAxis *liftOffsetAxis, - offsetAxis *tiltOffsetAxis) + auxiliaryAxis *liftOffsetAxis, + auxiliaryAxis *tiltOffsetAxis) : turboPmacAxis(pC, axisNo, false), pC_(pC) { /* @@ -77,6 +80,7 @@ detectorTowerAxis::detectorTowerAxis(detectorTowerController *pC, int axisNo, error_ = 0; targetPosition_ = 0.0; receivedTarget_ = false; + startingDeferredMovement_ = false; liftOffsetAxis_ = liftOffsetAxis; tiltOffsetAxis_ = tiltOffsetAxis; liftOffsetAxis->dTA_ = this; @@ -248,18 +252,32 @@ asynStatus detectorTowerAxis::doPoll(bool *moving) { // Store the axis status error_ = error; - // Update the enablement PV - pl_status = setIntegerParam(pC_->motorEnableRBV_, (positionState != 2)); + // Update the working position state PV + pl_status = setIntegerParam(pC_->positionStateRBV_, positionState); + if (pl_status != asynSuccess) { + return pC_->paramLibAccessFailed(pl_status, "positionStateRBV_", + axisNo_, __PRETTY_FUNCTION__, + __LINE__); + } + + // Axis is always enabled + pl_status = setIntegerParam(pC_->motorEnableRBV_, 1); if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorEnableRBV_", axisNo_, __PRETTY_FUNCTION__, __LINE__); } // Check if the motor is moving - if (inPosition == 0) { - *moving = false; - } else { + if (inPosition == 1) { + // By now, the controller has actually started the movement + startingDeferredMovement_ = false; *moving = true; + } else { + if (startingDeferredMovement_) { + *moving = true; + } else { + *moving = false; + } } // Create the unique callsite identifier manually so it can be used later in @@ -282,17 +300,28 @@ asynStatus detectorTowerAxis::doPoll(bool *moving) { if (pC_->msgPrintControl_.shouldBePrinted(keyPosState, true, pC_->pasynUserSelf)) { - 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()); + asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW, + "Controller \"%s\", axis %d => %s, line %d\nAxis moving " + "to or in change " + "position.%s\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, + pC_->msgPrintControl_.getSuffix()); + } + resetCountPosState = false; + break; + case 3: + if (pC_->msgPrintControl_.shouldBePrinted(keyPosState, true, + pC_->pasynUserSelf)) { + + asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW, + "Controller \"%s\", axis %d => %s, line %d\nAxis moving " + "to working position.%s\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, + pC_->msgPrintControl_.getSuffix()); } resetCountPosState = false; break; default: - *moving = false; if (pC_->msgPrintControl_.shouldBePrinted(keyPosState, true, pC_->pasynUserSelf)) { @@ -318,7 +347,7 @@ asynStatus detectorTowerAxis::doPoll(bool *moving) { } if (resetCountPosState) { - pC_->msgPrintControl_.resetCount(keyPosState); + pC_->msgPrintControl_.resetCount(keyPosState, pC_->pasynUserSelf); } if (*moving) { @@ -637,7 +666,7 @@ asynStatus detectorTowerAxis::doPoll(bool *moving) { } if (resetError) { - pC_->msgPrintControl_.resetCount(keyError); + pC_->msgPrintControl_.resetCount(keyError, pC_->pasynUserSelf); } // Update the parameter library @@ -723,12 +752,16 @@ asynStatus detectorTowerAxis::doPoll(bool *moving) { } int tiltAxisNo = tiltOffsetAxis_->axisNo_; - pl_status = pC_->setDoubleParam(tiltAxisNo, pC_->motorPosition_, - tiltOffsetAxis_->targetPosition_ / - motorRecResolution); + + // There exists no readback value for tiltOffsetAxis_, hence we just set the + // current position to the target position. + pl_status = tiltOffsetAxis_->setDoubleParam( + pC_->motorPosition_, + tiltOffsetAxis_->targetPosition_ / motorRecResolution); + if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorPosition_", - liftAxisNo, __PRETTY_FUNCTION__, + tiltAxisNo, __PRETTY_FUNCTION__, __LINE__); } pl_status = pC_->setDoubleParam(tiltAxisNo, pC_->motorHighLimitFromDriver_, @@ -755,10 +788,11 @@ asynStatus detectorTowerAxis::doPoll(bool *moving) { __PRETTY_FUNCTION__, __LINE__); } - pl_status = setIntegerParam(pC_->positionState_, positionState); + pl_status = setIntegerParam(pC_->positionStateRBV_, positionState); if (pl_status != asynSuccess) { - return pC_->paramLibAccessFailed(pl_status, "positionState_", axisNo_, - __PRETTY_FUNCTION__, __LINE__); + return pC_->paramLibAccessFailed(pl_status, "positionStateRBV_", + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } return poll_status; @@ -795,29 +829,9 @@ asynStatus detectorTowerAxis::startCombinedMove() { char command[pC_->MAXBUF_], response[pC_->MAXBUF_]; double motorCoordinatesPosition = 0.0; int positionState = 0; - int moving = 0; // ========================================================================= - pl_status = pC_->getIntegerParam(axisNo_, pC_->motorStatusMoving_, &moving); - if (pl_status != asynSuccess) { - return pC_->paramLibAccessFailed(pl_status, "motorStatusMoving_", - axisNo_, __PRETTY_FUNCTION__, - __LINE__); - } - - // Check if the axis is already moving. In this case, do nothing - if (moving) { - return asynSuccess; - } - - pl_status = - pC_->getIntegerParam(axisNo_, pC_->positionState_, &positionState); - if (pl_status != asynSuccess) { - return pC_->paramLibAccessFailed(pl_status, "positionState_", 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; @@ -833,6 +847,7 @@ asynStatus detectorTowerAxis::startCombinedMove() { pC_->msgPrintControl_.getSuffix()); } if (isInChangerPos) { + startingDeferredMovement_ = false; return asynError; } @@ -842,6 +857,7 @@ asynStatus detectorTowerAxis::startCombinedMove() { targetPosition_, liftOffsetAxis_->targetPosition_, tiltOffsetAxis_->targetPosition_); + // DEBUG asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "%s\n", command); // Lock the access to the controller since this function runs in another @@ -915,7 +931,8 @@ asynStatus detectorTowerAxis::readEncoderType() { return asynSuccess; } -asynStatus detectorTowerAxis::moveToWorkingPosition(bool toWorkingPosition) { +asynStatus +detectorTowerAxis::toggleWorkingChangerState(bool toChangingPosition) { char response[pC_->MAXBUF_]; @@ -933,28 +950,21 @@ asynStatus detectorTowerAxis::moveToWorkingPosition(bool toWorkingPosition) { doPoll(&moving); pl_status = - pC_->getIntegerParam(axisNo_, pC_->positionState_, &positionState); + pC_->getIntegerParam(axisNo_, pC_->positionStateRBV_, &positionState); if (pl_status != asynSuccess) { - return pC_->paramLibAccessFailed(pl_status, "positionState_", axisNo_, - __PRETTY_FUNCTION__, __LINE__); + return pC_->paramLibAccessFailed(pl_status, "positionStateRBV_", + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } - // If the axis is currently moving, it cannot be disabled. Ignore the - // 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 - // actually be disabled in this state! - - if (pC_->msgPrintControl_.shouldBePrinted(pC_->portName, axisNo_, - __PRETTY_FUNCTION__, __LINE__, - moving, pC_->pasynUserSelf)) { + if (moving) { asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "Controller \"%s\", axis %d => %s, line %d\nAxis is not " - "idle and can therefore not be moved to %s position.%s\n", + "idle and can therefore not be moved to %s state.%s\n", pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, - toWorkingPosition ? "working" : "changer", + toChangingPosition ? "changer" : "working", pC_->msgPrintControl_.getSuffix()); - } - if (moving) { + pl_status = setStringParam( pC_->motorMessageText_, "Axis cannot be moved to changer position while it is moving."); @@ -967,8 +977,8 @@ asynStatus detectorTowerAxis::moveToWorkingPosition(bool toWorkingPosition) { } // Axis is already in the correct position - bool isAlreadyThere = (toWorkingPosition == true && positionState == 1) || - (toWorkingPosition == false && positionState == 2); + bool isAlreadyThere = (toChangingPosition == false && positionState == 1) || + (toChangingPosition == true && positionState == 2); if (pC_->msgPrintControl_.shouldBePrinted( pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, @@ -977,7 +987,7 @@ asynStatus detectorTowerAxis::moveToWorkingPosition(bool toWorkingPosition) { "Controller \"%s\", axis %d => %s, line %d\nAxis is already " "in %s position.%s\n", pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, - toWorkingPosition ? "working" : "changer", + toChangingPosition ? "changer" : "working", pC_->msgPrintControl_.getSuffix()); } if (isAlreadyThere) { @@ -985,15 +995,14 @@ asynStatus detectorTowerAxis::moveToWorkingPosition(bool toWorkingPosition) { } // Move the axis into changer or working position - if (toWorkingPosition) { - rw_status = pC_->writeRead(axisNo_, "P350=3", response, 0); - pl_status = setStringParam(pC_->motorMessageText_, - "Moving to working position ..."); - - } else { + if (toChangingPosition) { rw_status = pC_->writeRead(axisNo_, "P350=2", response, 0); pl_status = setStringParam(pC_->motorMessageText_, "Moving to changer position ..."); + } else { + rw_status = pC_->writeRead(axisNo_, "P350=3", response, 0); + pl_status = setStringParam(pC_->motorMessageText_, + "Moving to working state ..."); } if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", @@ -1010,15 +1019,16 @@ asynStatus detectorTowerAxis::reset() { int positionState = 0; pl_status = - pC_->getIntegerParam(axisNo_, pC_->positionState_, &positionState); + pC_->getIntegerParam(axisNo_, pC_->positionStateRBV_, &positionState); if (pl_status != asynSuccess) { - return pC_->paramLibAccessFailed(pl_status, "positionState_", axisNo_, - __PRETTY_FUNCTION__, __LINE__); + return pC_->paramLibAccessFailed(pl_status, "positionStateRBV_", + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } /* Check which action should be performed: - - If positionState_ == 0 (not ready): P352 = 1 (Set axis in closed-loop + - If positionState == 0 (not ready): P352 = 1 (Set axis in closed-loop mode) - If error_ == 10 or 11 (FTZ motor error): P352 = 3 (Recover FTZ) - If any other error: P352 = 2 (Reset error) @@ -1107,8 +1117,8 @@ asynStatus detectorTowerCreateAxis(const char *portName, int axisDetectorTower, be "leaked" here. */ - offsetAxis *liftOffsetAxis = new offsetAxis(pC, liftOffsetaxisNo); - offsetAxis *tiltOffsetAxis = new offsetAxis(pC, tiltOffsetaxisNo); + auxiliaryAxis *liftOffsetAxis = new auxiliaryAxis(pC, liftOffsetaxisNo); + auxiliaryAxis *tiltOffsetAxis = new auxiliaryAxis(pC, tiltOffsetaxisNo); #pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-variable" diff --git a/src/detectorTowerAxis.h b/src/detectorTowerAxis.h index 86dfab4..885abd9 100644 --- a/src/detectorTowerAxis.h +++ b/src/detectorTowerAxis.h @@ -1,6 +1,6 @@ #ifndef detectorTowerAxis_H #define detectorTowerAxis_H -#include "offsetAxis.h" +#include "auxiliaryAxis.h" #include "turboPmacAxis.h" // Forward declaration of the controller class to resolve the cyclic dependency @@ -8,6 +8,10 @@ // https://en.cppreference.com/w/cpp/language/class. class detectorTowerController; +/** + * @brief + * + */ class detectorTowerAxis : public turboPmacAxis { public: /** @@ -15,12 +19,17 @@ class detectorTowerAxis : public turboPmacAxis { * * @param pController Pointer to the associated controller * @param axisNo Index of the axis + * @param liftOffsetAxis Pointer to the attached axis which controls + * the lift offset + * @param tiltOffsetAxis Pointer to the attached axis which controls + * the tilt offset */ detectorTowerAxis(detectorTowerController *pController, int axisNo, - offsetAxis *liftOffsetAxis, offsetAxis *tiltOffsetAxis); + auxiliaryAxis *liftOffsetAxis, + auxiliaryAxis *tiltOffsetAxis); /** - * @brief Destroy the turboPmacAxis + * @brief Destroy the detectorTowerAxis * */ virtual ~detectorTowerAxis(); @@ -87,7 +96,7 @@ class detectorTowerAxis : public turboPmacAxis { * * @return asynStatus */ - asynStatus moveToWorkingPosition(bool toWorkingPosition); + asynStatus toggleWorkingChangerState(bool toChangingPosition); /** * @brief Reset the axis error @@ -97,20 +106,25 @@ class detectorTowerAxis : public turboPmacAxis { */ asynStatus reset(); - // If true, either this axis or one of the offsetAxis attached to it + // If true, either this axis or one of the auxiliaryAxis attached to it // received a movement command. bool receivedTarget_; + // If set to true, the virtual axis is about to start a deferred movement + // (but is currently still collecting movement commands from its component + // axes) + bool startingDeferredMovement_; + protected: detectorTowerController *pC_; double targetPosition_; int error_; - offsetAxis *tiltOffsetAxis_; - offsetAxis *liftOffsetAxis_; + auxiliaryAxis *tiltOffsetAxis_; + auxiliaryAxis *liftOffsetAxis_; private: friend class detectorTowerController; - friend class offsetAxis; + friend class auxiliaryAxis; }; #endif diff --git a/src/detectorTowerController.cpp b/src/detectorTowerController.cpp index bcfd89c..ad770de 100644 --- a/src/detectorTowerController.cpp +++ b/src/detectorTowerController.cpp @@ -6,7 +6,7 @@ #include /** - * @brief Construct a new turboPmacController::turboPmacController object + * @brief Construct a new detectorTowerController object * * @param portName See documentation of sinqController * @param ipPortConfigName See documentation of sinqController @@ -27,7 +27,8 @@ detectorTowerController::detectorTowerController( asynStatus status = asynSuccess; - status = createParam("POSITION_STATE", asynParamInt32, &positionState_); + status = + createParam("POSITION_STATE_RBV", asynParamInt32, &positionStateRBV_); if (status != asynSuccess) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "Controller \"%s\" => %s, line %d\nFATAL ERROR (creating a " @@ -37,18 +38,7 @@ detectorTowerController::detectorTowerController( 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_); + status = createParam("CHANGE_STATE", asynParamInt32, &changeState_); if (status != asynSuccess) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "Controller \"%s\" => %s, line %d\nFATAL ERROR (creating a " @@ -65,7 +55,7 @@ asynStatus detectorTowerController::readInt32(asynUser *pasynUser, // Check if the axis is a detectorTowerAxis detectorTowerAxis *axis = getDetectorTowerAxis(pasynUser); if (axis == nullptr) { - // This is apparently a "normal" turboPmacAxis or an offsetAxis + // This is apparently a "normal" turboPmacAxis or an auxiliaryAxis return turboPmacController::readInt32(pasynUser, value); } else { // The detector tower cannot be disabled @@ -87,13 +77,11 @@ asynStatus detectorTowerController::writeInt32(asynUser *pasynUser, detectorTowerAxis *axis = getDetectorTowerAxis(pasynUser); if (axis == nullptr) { - // This is apparently a "normal" turboPmacAxis or an offsetAxis + // This is apparently a "normal" turboPmacAxis or an auxiliaryAxis return turboPmacController::writeInt32(pasynUser, value); } else { - if (function == moveToWorkingPosition_) { - return axis->moveToWorkingPosition(value != 0); - } else if (function == resetError_) { - return axis->reset(); + if (function == changeState_) { + return axis->toggleWorkingChangerState(value); } else { return turboPmacController::writeInt32(pasynUser, value); } diff --git a/src/detectorTowerController.h b/src/detectorTowerController.h index c32755c..3ab483f 100644 --- a/src/detectorTowerController.h +++ b/src/detectorTowerController.h @@ -8,8 +8,8 @@ #ifndef detectorTowerController_H #define detectorTowerController_H +#include "auxiliaryAxis.h" #include "detectorTowerAxis.h" -#include "offsetAxis.h" #include "turboPmacController.h" class detectorTowerController : public turboPmacController { @@ -76,14 +76,13 @@ class detectorTowerController : public turboPmacController { private: // Indices of additional PVs -#define FIRST_detectorTower_PARAM positionState_ - int positionState_; - int moveToWorkingPosition_; - int resetError_; -#define LAST_detectorTower_PARAM resetError_ +#define FIRST_detectorTower_PARAM positionStateRBV_ + int positionStateRBV_; + int changeState_; +#define LAST_detectorTower_PARAM changeState_ friend class detectorTowerAxis; - friend class offsetAxis; + friend class auxiliaryAxis; }; #define NUM_detectorTower_DRIVER_PARAMS \ (&LAST_detectorTower_PARAM - &FIRST_detectorTower_PARAM + 1)