diff --git a/Makefile b/Makefile index 3e63db9..b883534 100644 --- a/Makefile +++ b/Makefile @@ -10,8 +10,11 @@ ARCH_FILTER=RHEL% REQUIRED+=asynMotor REQUIRED+=sinqMotor +# Specify the version of asynMotor we want to build against +asynMotor_VERSION=7.2.2 + # Specify the version of sinqMotor we want to build against -sinqMotor_VERSION=0.6.3 +sinqMotor_VERSION=0.7.0 # These headers allow to depend on this library for derived drivers. HEADERS += src/turboPmacAxis.h @@ -27,4 +30,4 @@ TEMPLATES += db/turboPmac.db # This file registers the motor-specific functions in the IOC shell. DBDS += src/turboPmac.dbd -USR_CFLAGS += -Wall -Wextra -Weffc++ -Wunused-result # -Werror +USR_CFLAGS += -Wall -Wextra -Weffc++ -Wunused-result -Wextra -Werror # -Wpedantic // Does not work because EPICS macros trigger warnings diff --git a/src/turboPmac.dbd b/src/turboPmac.dbd index 8336f52..0031597 100644 --- a/src/turboPmac.dbd +++ b/src/turboPmac.dbd @@ -1,4 +1,5 @@ #--------------------------------------------- # SINQ specific DB definitions #--------------------------------------------- -registrar(turboPmacRegister) +registrar(turboPmacControllerRegister) +registrar(turboPmacAxisRegister) \ No newline at end of file diff --git a/src/turboPmacAxis.cpp b/src/turboPmacAxis.cpp index db53888..96e8875 100644 --- a/src/turboPmacAxis.cpp +++ b/src/turboPmacAxis.cpp @@ -1,12 +1,39 @@ #include "turboPmacAxis.h" #include "asynOctetSyncIO.h" +#include "epicsExport.h" +#include "iocsh.h" #include "turboPmacController.h" #include #include +#include #include #include #include #include +#include + +/* +Contains all instances of turboPmacAxis which have been created and is used in +the initialization hook function. + */ +static std::vector axes; + +/** + * @brief Hook function to perform certain actions during the IOC initialization + * + * @param iState + */ +static void epicsInithookFunction(initHookState iState) { + if (iState == initHookAfterDatabaseRunning) { + // Iterate through all axes of each and call the initialization method + // on each one of them. + for (std::vector::iterator itA = axes.begin(); + itA != axes.end(); ++itA) { + turboPmacAxis *axis = *itA; + axis->init(); + } + } +} turboPmacAxis::turboPmacAxis(turboPmacController *pC, int axisNo) : sinqAxis(pC, axisNo), pC_(pC) { @@ -28,14 +55,23 @@ turboPmacAxis::turboPmacAxis(turboPmacController *pC, int axisNo) */ if (axisNo >= pC->numAxes_) { asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, - "%s => line %d:: FATAL ERROR: Axis index %d must be smaller " - "than the total number of axes %d. Call the support.", - __PRETTY_FUNCTION__, __LINE__, axisNo_, pC->numAxes_); + "Controller \"%s\", axis %d => %s, line %d: FATAL ERROR: " + "Axis index %d must be smaller than the total number of axes " + "%d", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, + axisNo_, pC->numAxes_); exit(-1); } + // Register the hook function during construction of the first axis object + if (axes.empty()) { + initHookRegister(&epicsInithookFunction); + } + + // Collect all axes into this list which will be used in the hook function + axes.push_back(this); + // Initialize all member variables - initial_poll_ = true; waitForHandshake_ = false; axisStatus_ = 0; @@ -44,9 +80,10 @@ turboPmacAxis::turboPmacAxis(turboPmacController *pC, int axisNo) if (status != asynSuccess) { asynPrint( pC_->pasynUserSelf, ASYN_TRACE_ERROR, - "%s => line %d:\nFATAL ERROR (setting a parameter value failed " - "with %s)\n. Terminating IOC", - __PRETTY_FUNCTION__, __LINE__, pC_->stringifyAsynStatus(status)); + "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); } @@ -55,9 +92,10 @@ turboPmacAxis::turboPmacAxis(turboPmacController *pC, int axisNo) if (status != asynSuccess) { asynPrint( pC_->pasynUserSelf, ASYN_TRACE_ERROR, - "%s => line %d:\nFATAL ERROR (setting a parameter value failed " - "with %s)\n. Terminating IOC", - __PRETTY_FUNCTION__, __LINE__, pC_->stringifyAsynStatus(status)); + "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); } @@ -74,7 +112,7 @@ turboPmacAxis::~turboPmacAxis(void) { /** Read the configuration at the first poll */ -asynStatus turboPmacAxis::atFirstPoll() { +asynStatus turboPmacAxis::init() { // Local variable declaration asynStatus status = asynSuccess; @@ -89,13 +127,29 @@ asynStatus turboPmacAxis::atFirstPoll() { // the air cushions in milliseconds. int axStatus = 0; - status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution_, - &motorRecResolution); - if (status == asynParamUndefined) { - return asynParamUndefined; - } else if (status != asynSuccess) { - return pC_->paramLibAccessFailed(status, "motorRecResolution_", - __PRETTY_FUNCTION__, __LINE__); + // 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__); + } } /* @@ -131,7 +185,7 @@ asynStatus turboPmacAxis::atFirstPoll() { // Store these values in the parameter library status = pC_->setDoubleParam(axisNo_, pC_->motorPosition_, motorPosition); if (status != asynSuccess) { - return pC_->paramLibAccessFailed(status, "motorPosition_", + return pC_->paramLibAccessFailed(status, "motorPosition_", axisNo_, __PRETTY_FUNCTION__, __LINE__); } @@ -152,9 +206,10 @@ asynStatus turboPmacAxis::atFirstPoll() { // sense to try and upstream this to the user -> Just log the error asynPrint( pC_->pasynUserSelf, ASYN_TRACE_ERROR, - "%s => line %d:\ncallParamCallbacks failed with %s for axis %d.\n", - __PRETTY_FUNCTION__, __LINE__, pC_->stringifyAsynStatus(status), - axisNo_); + "Controller \"%s\", axis %d => %s, line %d\ncallParamCallbacks " + "failed with %s.\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, + pC_->stringifyAsynStatus(status)); return status; } @@ -216,9 +271,24 @@ asynStatus turboPmacAxis::doPoll(bool *moving) { // poll waitForHandshake_ = false; } else { - // Still waiting for the handshake. This is already part of the - // movement procedure! + // Still waiting for the handshake - try again in the next busy + // poll. This is already part of the movement procedure. *moving = true; + + pl_status = setIntegerParam(pC_->motorStatusMoving_, *moving); + if (pl_status != asynSuccess) { + return pC_->paramLibAccessFailed(pl_status, + "motorStatusMoving_", axisNo_, + __PRETTY_FUNCTION__, __LINE__); + } + + pl_status = setIntegerParam(pC_->motorStatusDone_, !(*moving)); + if (pl_status != asynSuccess) { + return pC_->paramLibAccessFailed(pl_status, "motorStatusDone_", + axisNo_, __PRETTY_FUNCTION__, + __LINE__); + } + return asynSuccess; } } @@ -228,14 +298,15 @@ asynStatus turboPmacAxis::doPoll(bool *moving) { &motorRecResolution); if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorRecResolution_", - __PRETTY_FUNCTION__, __LINE__); + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } // Read the previous motor position pl_status = pC_->getDoubleParam(axisNo_, pC_->motorPosition_, &previousPosition); if (pl_status != asynSuccess) { - return pC_->paramLibAccessFailed(pl_status, "motorPosition_", + return pC_->paramLibAccessFailed(pl_status, "motorPosition_", axisNo_, __PRETTY_FUNCTION__, __LINE__); } @@ -275,7 +346,8 @@ asynStatus turboPmacAxis::doPoll(bool *moving) { pC_->getDoubleParam(axisNo_, pC_->motorLimitsOffset_, &limitsOffset); if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorLimitsOffset_", - __PRETTY_FUNCTION__, __LINE__); + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } highLimit = highLimit - limitsOffset; lowLimit = lowLimit + limitsOffset; @@ -287,7 +359,7 @@ asynStatus turboPmacAxis::doPoll(bool *moving) { pl_status = setIntegerParam(pC_->motorEnableRBV_, (axStatus != -3 && axStatus != -5)); if (pl_status != asynSuccess) { - return pC_->paramLibAccessFailed(pl_status, "motorEnableRBV_", + return pC_->paramLibAccessFailed(pl_status, "motorEnableRBV_", axisNo_, __PRETTY_FUNCTION__, __LINE__); } @@ -296,110 +368,123 @@ asynStatus turboPmacAxis::doPoll(bool *moving) { case -6: *moving = true; - asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW, - "%s => line %d:\nAxis %d is stopping\n", __PRETTY_FUNCTION__, - __LINE__, axisNo_); + asynPrint( + pC_->pasynUserSelf, ASYN_TRACE_FLOW, + "Controller \"%s\", axis %d => %s, line %d\nAxis is stopping\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); break; case -5: *moving = false; asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW, - "%s => line %d:\nAxis %d is deactivated\n", - __PRETTY_FUNCTION__, __LINE__, axisNo_); + "Controller \"%s\", axis %d => %s, line %d\nAxis is " + "deactivated\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); pl_status = setStringParam(pC_->motorMessageText_, "Deactivated"); if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", - __PRETTY_FUNCTION__, __LINE__); + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } - // No further evaluation of the axis status is necessary - return asynSuccess; + break; case -4: *moving = false; asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, - "%s => line %d:\nEmergency stop activated\n", - __PRETTY_FUNCTION__, __LINE__); + "Controller \"%s\", axis %d => %s, line %d\nEmergency stop " + "activated\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); pl_status = setStringParam(pC_->motorMessageText_, "Emergency stop"); if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", - __PRETTY_FUNCTION__, __LINE__); + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } - // No further evaluation of the axis status is necessary - return asynSuccess; + break; case -3: *moving = false; - asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW, - "%s => line %d:\nAxis %d is disabled\n", __PRETTY_FUNCTION__, - __LINE__, axisNo_); + asynPrint( + pC_->pasynUserSelf, ASYN_TRACE_FLOW, + "Controller \"%s\", axis %d => %s, line %d\nAxis %d is disabled\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, axisNo_); pl_status = setStringParam(pC_->motorMessageText_, "Disabled"); if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", - __PRETTY_FUNCTION__, __LINE__); + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } - // No further evaluation of the axis status is necessary - return asynSuccess; + break; case 0: *moving = false; asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW, - "%s => line %d:\nAxis %d is idle\n", __PRETTY_FUNCTION__, - __LINE__, axisNo_); + "Controller \"%s\", axis %d => %s, line %d\nAxis is idle\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); break; case 1: *moving = true; asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW, - "%s => line %d:\nMove order for axis %d acknowledged\n", - __PRETTY_FUNCTION__, __LINE__, axisNo_); + "Controller \"%s\", axis %d => %s, line %d\nMove order " + "acknowledged\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); break; case 2: *moving = true; asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW, - "%s => line %d:\nMove order for axis %d is possible\n", - __PRETTY_FUNCTION__, __LINE__, axisNo_); + "Controller \"%s\", axis %d => %s, line %d\nMove order is " + "possible\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); break; case 3: *moving = true; asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW, - "%s => line %d:\nAxis %d in Air Cushion Output status\n", - __PRETTY_FUNCTION__, __LINE__, axisNo_); + "Controller \"%s\", axis %d => %s, line %d\nAxis in Air " + "Cushion Output status\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); break; case 4: *moving = true; asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW, - "%s => line %d:\nAxis %d in Air Cushion Input status\n", - __PRETTY_FUNCTION__, __LINE__, axisNo_); + "Controller \"%s\", axis %d => %s, line %d\nAxis in Air " + "Cushion Input status\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); break; case 5: *moving = true; - asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW, - "%s => line %d:\nAxis %d is moving\n", __PRETTY_FUNCTION__, - __LINE__, axisNo_); + asynPrint( + pC_->pasynUserSelf, ASYN_TRACE_FLOW, + "Controller \"%s\", axis %d => %s, line %d\nAxis is moving\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); break; default: *moving = false; - asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, - "%s => line %d:\nReached unreachable state P%2.2d00 = %d\n", - __PRETTY_FUNCTION__, __LINE__, axisNo_, axStatus); + asynPrint( + pC_->pasynUserSelf, ASYN_TRACE_ERROR, + "Controller \"%s\", axis %d => %s, line %d\nReached unreachable " + "state P%2.2d00 = %d\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, axisNo_, + axStatus); pl_status = setStringParam( pC_->motorMessageText_, "Unreachable state has been reached. Please call the support."); if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", - __PRETTY_FUNCTION__, __LINE__); + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } } @@ -420,17 +505,19 @@ asynStatus turboPmacAxis::doPoll(bool *moving) { case 1: // EPICS should already prevent this issue in the first place, // since it contains the user limits - asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, - "%s => line %d:\nTarget position would exceed user limits in " - "axis %d\n", - __PRETTY_FUNCTION__, __LINE__, axisNo_); + asynPrint( + pC_->pasynUserSelf, ASYN_TRACE_ERROR, + "Controller \"%s\", axis %d => %s, line %d\nTarget position would " + "exceed user limits.\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); pl_status = setStringParam(pC_->motorMessageText_, "Target position would exceed software " "limits. Please call the support."); if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", - __PRETTY_FUNCTION__, __LINE__); + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } poll_status = asynError; @@ -438,26 +525,29 @@ asynStatus turboPmacAxis::doPoll(bool *moving) { case 5: // Command not possible asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, - "%s => line %d:\nAxis %d is still moving, but received " - "another move command. EPICS should prevent this, check if " - "*moving is set correctly.\n", - __PRETTY_FUNCTION__, __LINE__, axisNo_); + "Controller \"%s\", axis %d => %s, line %d\nAxis is " + "still moving, but received another move command. EPICS " + "should prevent this, check if *moving is set correctly.\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); pl_status = setStringParam(pC_->motorMessageText_, "Axis received move command while it is " "still moving. Please call the support."); if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", - __PRETTY_FUNCTION__, __LINE__); + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } poll_status = asynError; break; case 8: - asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, - "%s => line %d:\nAir cushion feedback stopped during " - "movement (P%2.2d01 = %d).\n", - __PRETTY_FUNCTION__, __LINE__, axisNo_, error); + asynPrint( + pC_->pasynUserSelf, ASYN_TRACE_ERROR, + "Controller \"%s\", axis %d => %s, line %d\nAir cushion feedback " + "stopped during movement (P%2.2d01 = %d).\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, axisNo_, + error); snprintf(userMessage, sizeof(userMessage), "Air cushion feedback stopped during movement (P%2.2d01 = " @@ -467,14 +557,16 @@ asynStatus turboPmacAxis::doPoll(bool *moving) { pl_status = setStringParam(pC_->motorMessageText_, userMessage); if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", - __PRETTY_FUNCTION__, __LINE__); + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } break; case 9: asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, - "%s => line %d:\nNo air cushion feedback before movement " - "start (P%2.2d01 = %d).\n", - __PRETTY_FUNCTION__, __LINE__, axisNo_, error); + "Controller \"%s\", axis %d => %s, line %d\nNo air cushion " + "feedback before movement start (P%2.2d01 = %d).\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, + axisNo_, error); snprintf(userMessage, sizeof(userMessage), "No air cushion feedback before movement start (P%2.2d01 = " @@ -483,7 +575,8 @@ asynStatus turboPmacAxis::doPoll(bool *moving) { pl_status = setStringParam(pC_->motorMessageText_, userMessage); if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", - __PRETTY_FUNCTION__, __LINE__); + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } break; case 10: @@ -494,8 +587,9 @@ asynStatus turboPmacAxis::doPoll(bool *moving) { which is not properly homed or if a bug occured. */ asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, - "%s => line %d:\nAxis %d hit the controller limits.\n", - __PRETTY_FUNCTION__, __LINE__, axisNo_); + "Controller \"%s\", axis %d => %s, line %d\nAxis hit the " + "controller limits.\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); snprintf(userMessage, sizeof(userMessage), "Software limits or end switch hit (P%2.2d01 = %d). Try " @@ -507,7 +601,8 @@ asynStatus turboPmacAxis::doPoll(bool *moving) { pl_status = setStringParam(pC_->motorMessageText_, userMessage); if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", - __PRETTY_FUNCTION__, __LINE__); + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } poll_status = asynError; @@ -515,9 +610,9 @@ asynStatus turboPmacAxis::doPoll(bool *moving) { case 11: // Following error asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, - "%s => line %d:\nMaximum allowed following error exceeded " - "for axis %d.\n", - __PRETTY_FUNCTION__, __LINE__, axisNo_); + "Controller \"%s\", axis %d => %s, line %d\nMaximum allowed " + "following error exceeded.\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); snprintf(command, sizeof(command), "Maximum allowed following error exceeded (P%2.2d01 = %d). " @@ -527,7 +622,8 @@ asynStatus turboPmacAxis::doPoll(bool *moving) { pl_status = setStringParam(pC_->motorMessageText_, command); if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", - __PRETTY_FUNCTION__, __LINE__); + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } poll_status = asynError; @@ -539,14 +635,23 @@ asynStatus turboPmacAxis::doPoll(bool *moving) { "for errors (if available). Otherwise please call " "the support.", axisNo_, error); + pl_status = setStringParam(pC_->motorMessageText_, command); + if (pl_status != asynSuccess) { + return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", + axisNo_, __PRETTY_FUNCTION__, + __LINE__); + } + + poll_status = asynError; break; case 13: // Driver hardware error triggered asynPrint( pC_->pasynUserSelf, ASYN_TRACE_ERROR, - "%s => line %d:\nDriver hardware error triggered for axis %d.\n", - __PRETTY_FUNCTION__, __LINE__, axisNo_); + "Controller \"%s\", axis %d => %s, line %d\nDriver hardware error " + "triggered.\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); snprintf(command, sizeof(command), "Driver hardware error (P%2.2d01 = 13). " "Please call the support.", @@ -554,7 +659,8 @@ asynStatus turboPmacAxis::doPoll(bool *moving) { pl_status = setStringParam(pC_->motorMessageText_, command); if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", - __PRETTY_FUNCTION__, __LINE__); + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } poll_status = asynError; @@ -562,10 +668,12 @@ asynStatus turboPmacAxis::doPoll(bool *moving) { case 14: // EPICS should already prevent this issue in the first place, // since it contains the user limits - asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, - "%s => line %d:\nMove command exceeds hardware limits " - "(P%2.2d01 = %d).\n", - __PRETTY_FUNCTION__, __LINE__, axisNo_, error); + asynPrint( + pC_->pasynUserSelf, ASYN_TRACE_ERROR, + "Controller \"%s\", axis %d => %s, line %d\nMove command exceeds " + "hardware limits (P%2.2d01 = %d).\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, axisNo_, + error); snprintf(command, sizeof(command), "Move command exceeds hardware limits (P%2.2d01 = %d). Please " @@ -574,24 +682,26 @@ asynStatus turboPmacAxis::doPoll(bool *moving) { pl_status = setStringParam(pC_->motorMessageText_, command); if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", - __PRETTY_FUNCTION__, __LINE__); + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } poll_status = asynError; break; default: asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, - "%s => line %d:\nAxis %d reached an unreachable state " - "(P%2.2d01 = %d).\n", - __PRETTY_FUNCTION__, __LINE__, axisNo_, axisNo_, error); + "Controller \"%s\", axis %d => %s, line %d\nUnknown error " + "P%2.2d01 = %d.\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, + axisNo_, error); pl_status = setStringParam( pC_->motorMessageText_, - "Axis reached an unreachable state (P%2.2d01 = %d). Please " - "call the support."); + "Unknown error P%2.2d01 = %d. Please call the support."); if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", - __PRETTY_FUNCTION__, __LINE__); + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } poll_status = asynError; @@ -603,7 +713,8 @@ asynStatus turboPmacAxis::doPoll(bool *moving) { pl_status = setIntegerParam(pC_->motorStatusProblem_, true); if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_", - __PRETTY_FUNCTION__, __LINE__); + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } } @@ -611,57 +722,62 @@ asynStatus turboPmacAxis::doPoll(bool *moving) { pl_status = setIntegerParam(pC_->motorMoveToHome_, 0); if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorMoveToHome_", - __PRETTY_FUNCTION__, __LINE__); + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } } pl_status = setIntegerParam(pC_->motorStatusMoving_, *moving); if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorStatusMoving_", - __PRETTY_FUNCTION__, __LINE__); + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } pl_status = setIntegerParam(pC_->motorStatusDone_, !(*moving)); if (pl_status != asynSuccess) { - return pC_->paramLibAccessFailed(pl_status, "motorStatusDone_", + return pC_->paramLibAccessFailed(pl_status, "motorStatusDone_", axisNo_, __PRETTY_FUNCTION__, __LINE__); } pl_status = setIntegerParam(pC_->motorStatusDirection_, direction); if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorStatusDirection_", - __PRETTY_FUNCTION__, __LINE__); + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } pl_status = pC_->setDoubleParam(axisNo_, pC_->motorHighLimitFromDriver_, highLimit); if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorHighLimitFromDriver_", - __PRETTY_FUNCTION__, __LINE__); + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } pl_status = pC_->setDoubleParam(axisNo_, pC_->motorLowLimitFromDriver_, lowLimit); if (pl_status != asynSuccess) { - return pC_->paramLibAccessFailed(pl_status, "motorLowLimit_", + return pC_->paramLibAccessFailed(pl_status, "motorLowLimit_", axisNo_, __PRETTY_FUNCTION__, __LINE__); } // Transform from motor to EPICS coordinates (see comment in - // turboPmacAxis::atFirstPoll()) + // turboPmacAxis::init()) currentPosition = currentPosition / motorRecResolution; pl_status = setDoubleParam(pC_->motorPosition_, currentPosition); if (pl_status != asynSuccess) { - return pC_->paramLibAccessFailed(pl_status, "motorPosition_", + return pC_->paramLibAccessFailed(pl_status, "motorPosition_", axisNo_, __PRETTY_FUNCTION__, __LINE__); } return poll_status; } -asynStatus turboPmacAxis::doMove(double position, int relative, double minVelocity, - double maxVelocity, double acceleration) { +asynStatus turboPmacAxis::doMove(double position, int relative, + double minVelocity, double maxVelocity, + double acceleration) { // Status of read-write-operations of ASCII commands to the controller asynStatus rw_status = asynSuccess; @@ -681,7 +797,7 @@ asynStatus turboPmacAxis::doMove(double position, int relative, double minVeloci pl_status = pC_->getIntegerParam(axisNo_, pC_->motorEnableRBV_, &enabled); if (pl_status != asynSuccess) { - return pC_->paramLibAccessFailed(pl_status, "enableMotorRBV_", + return pC_->paramLibAccessFailed(pl_status, "enableMotorRBV_", axisNo_, __PRETTY_FUNCTION__, __LINE__); } @@ -689,13 +805,15 @@ asynStatus turboPmacAxis::doMove(double position, int relative, double minVeloci &motorRecResolution); if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorRecResolution_", - __PRETTY_FUNCTION__, __LINE__); + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } if (enabled == 0) { - asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, - "%s => line %d:\nAxis %d is disabled.\n", __PRETTY_FUNCTION__, - __LINE__, axisNo_); + asynPrint( + pC_->pasynUserSelf, ASYN_TRACE_ERROR, + "Controller \"%s\", axis %d => %s, line %d\nAxis is disabled.\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); return asynSuccess; } @@ -704,15 +822,17 @@ asynStatus turboPmacAxis::doMove(double position, int relative, double minVeloci motorVelocity = maxVelocity * motorRecResolution; asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW, - "%s => line %d:\nStart of axis %d to position %lf.\n", - __PRETTY_FUNCTION__, __LINE__, axisNo_, position); + "Controller \"%s\", axis %d => %s, line %d\nStart of axis to " + "position %lf.\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, position); // Check if the speed is allowed to be changed pl_status = pC_->getIntegerParam(axisNo_, pC_->motorCanSetSpeed_, &motorCanSetSpeed); if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorCanSetSpeed_", - __PRETTY_FUNCTION__, __LINE__); + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } // Prepend the new motor speed, if the user is allowed to set the speed. @@ -724,8 +844,10 @@ asynStatus turboPmacAxis::doMove(double position, int relative, double minVeloci writeOffset = strlen(command); asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW, - "%s => line %d:\nSetting speed of axis %d to %lf.\n", - __PRETTY_FUNCTION__, __LINE__, axisNo_, motorVelocity); + "Controller \"%s\", axis %d => %s, line %d\nSetting speed " + "to %lf.\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, + motorVelocity); } // Perform handshake, Set target position (and speed, if allowed) and start @@ -744,15 +866,17 @@ asynStatus turboPmacAxis::doMove(double position, int relative, double minVeloci rw_status = pC_->writeRead(axisNo_, command, response, 0); if (rw_status != asynSuccess) { - asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, - "%s => line %d:\nStarting movement to target position %lf " - "failed for axis %d.\n", - __PRETTY_FUNCTION__, __LINE__, motorCoordinatesPosition, - axisNo_); + asynPrint( + pC_->pasynUserSelf, ASYN_TRACE_ERROR, + "Controller \"%s\", axis %d => %s, line %d\nStarting movement to " + "target position %lf failed.\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, + motorCoordinatesPosition); pl_status = setIntegerParam(pC_->motorStatusProblem_, true); if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_", - __PRETTY_FUNCTION__, __LINE__); + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } return rw_status; } @@ -787,14 +911,17 @@ asynStatus turboPmacAxis::stop(double acceleration) { rw_status = pC_->writeRead(axisNo_, command, response, 0); if (rw_status != asynSuccess) { - asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, - "%s => line %d:\nStopping the movement failed for axis %d.\n", - __PRETTY_FUNCTION__, __LINE__, axisNo_); + asynPrint( + pC_->pasynUserSelf, ASYN_TRACE_ERROR, + "Controller \"%s\", axis %d => %s, line %d\nStopping the movement " + "failed.\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); pl_status = setIntegerParam(pC_->motorStatusProblem_, true); if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_", - __PRETTY_FUNCTION__, __LINE__); + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } } @@ -805,7 +932,7 @@ asynStatus turboPmacAxis::stop(double acceleration) { Home the axis. On absolute encoder systems, this is a no-op */ asynStatus turboPmacAxis::doHome(double min_velocity, double max_velocity, - double acceleration, int forwards) { + double acceleration, int forwards) { // Status of read-write-operations of ASCII commands to the controller asynStatus rw_status = asynSuccess; @@ -820,7 +947,7 @@ asynStatus turboPmacAxis::doHome(double min_velocity, double max_velocity, pl_status = pC_->getStringParam(axisNo_, pC_->encoderType_, sizeof(response), response); if (pl_status != asynSuccess) { - return pC_->paramLibAccessFailed(pl_status, "encoderType_", + return pC_->paramLibAccessFailed(pl_status, "encoderType_", axisNo_, __PRETTY_FUNCTION__, __LINE__); } @@ -836,24 +963,28 @@ asynStatus turboPmacAxis::doHome(double min_velocity, double max_velocity, pl_status = setIntegerParam(pC_->motorMoveToHome_, 1); if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorMoveToHome_", - __PRETTY_FUNCTION__, __LINE__); + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } pl_status = setStringParam(pC_->motorMessageText_, "Homing"); if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", - __PRETTY_FUNCTION__, __LINE__); + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } + return callParamCallbacks(); } else { pl_status = setStringParam(pC_->motorMessageText_, "Can't home a motor with absolute encoder"); if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", - __PRETTY_FUNCTION__, __LINE__); + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } } - return rw_status; + return asynSuccess; } /* @@ -874,7 +1005,7 @@ asynStatus turboPmacAxis::readEncoderType() { // ========================================================================= // Check if this is an absolute encoder - snprintf(command, sizeof(command), "I%2.2X04", axisNo_); + snprintf(command, sizeof(command), "I%2.2d04", axisNo_); rw_status = pC_->writeRead(axisNo_, command, response, 1); if (rw_status != asynSuccess) { return rw_status; @@ -909,7 +1040,7 @@ asynStatus turboPmacAxis::readEncoderType() { } if (pl_status != asynSuccess) { - return pC_->paramLibAccessFailed(pl_status, "encoderType_", + return pC_->paramLibAccessFailed(pl_status, "encoderType_", axisNo_, __PRETTY_FUNCTION__, __LINE__); } return asynSuccess; @@ -945,16 +1076,16 @@ asynStatus turboPmacAxis::rereadEncoder() { pl_status = pC_->getStringParam(axisNo_, pC_->encoderType_, sizeof(encoderType), encoderType); if (pl_status != asynSuccess) { - return pC_->paramLibAccessFailed(pl_status, "encoderType_", + return pC_->paramLibAccessFailed(pl_status, "encoderType_", axisNo_, __PRETTY_FUNCTION__, __LINE__); } // Abort if the axis is incremental if (strcmp(encoderType, IncrementalEncoder) == 0) { asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW, - "%s => line %d:\nEncoder of axis %d is not reread because it " - "is incremental.\n", - __PRETTY_FUNCTION__, __LINE__, axisNo_); + "Controller \"%s\", axis %d => %s, line %d\nEncoder is " + "not reread because it is incremental.\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); return asynSuccess; } @@ -963,30 +1094,31 @@ asynStatus turboPmacAxis::rereadEncoder() { int enabled = 0; pl_status = pC_->getIntegerParam(axisNo_, pC_->motorEnableRBV_, &enabled); if (pl_status != asynSuccess) { - return pC_->paramLibAccessFailed(pl_status, "enableMotorRBV_", + return pC_->paramLibAccessFailed(pl_status, "enableMotorRBV_", axisNo_, __PRETTY_FUNCTION__, __LINE__); } if (enabled == 1) { asynPrint(pC_->pasynUserSelf, ASYN_TRACE_WARNING, - "%s => line %d:\nAxis %d on controller %s must be " + "Controller \"%s\", axis %d => %s, line %d\nAxis must be " "disabled before rereading the encoder.\n", - __PRETTY_FUNCTION__, __LINE__, axisNo_, pC_->portName); + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); pl_status = setStringParam( pC_->motorMessageText_, "Axis must be disabled before rereading the encoder."); if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", - __PRETTY_FUNCTION__, __LINE__); + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } return asynError; } else { snprintf(command, sizeof(command), "M%2.2d=15", axisNo_); - asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW, - "%s => line %d:\nRereading absolute encoder of axis %d on " - "controller %s via command %s.\n", - __PRETTY_FUNCTION__, __LINE__, axisNo_, pC_->portName, - command); + asynPrint( + pC_->pasynUserSelf, ASYN_TRACE_FLOW, + "Controller \"%s\", axis %d => %s, line %d\nRereading absolute " + "encoder via command %s.\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, command); pC_->writeRead(axisNo_, command, response, 0); } @@ -1001,7 +1133,8 @@ asynStatus turboPmacAxis::rereadEncoder() { pl_status = pC_->setIntegerParam(pC_->rereadEncoderPosition_, 0); if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "rereadEncoderPosition_", - __PRETTY_FUNCTION__, __LINE__); + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } return asynSuccess; } @@ -1030,16 +1163,17 @@ asynStatus turboPmacAxis::enable(bool on) { if (axisStatus_ == 1 || axisStatus_ == 2 || axisStatus_ == 3 || axisStatus_ == 4 || axisStatus_ == 5) { asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, - "%s => line %d:\nAxis %d is not idle and can therefore not " - "be enabled / disabled.\n", - __PRETTY_FUNCTION__, __LINE__, axisNo_); + "Controller \"%s\", axis %d => %s, line %d\nAxis is not " + "idle and can therefore not be enabled / disabled.\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); pl_status = setStringParam(pC_->motorMessageText_, "Axis cannot be disabled while it is moving."); if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", - __PRETTY_FUNCTION__, __LINE__); + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } return asynError; @@ -1048,10 +1182,11 @@ asynStatus turboPmacAxis::enable(bool on) { // Axis is already enabled / disabled and a new enable / disable command // was sent => Do nothing if ((axisStatus_ != -3) == on) { - asynPrint(pC_->pasynUserSelf, ASYN_TRACE_WARNING, - "%s => line %d:\nAxis %d on controller %s is already %s.\n", - __PRETTY_FUNCTION__, __LINE__, axisNo_, pC_->portName, - on ? "enabled" : "disabled"); + asynPrint( + pC_->pasynUserSelf, ASYN_TRACE_WARNING, + "Controller \"%s\", axis %d => %s, line %d\nAxis is already %s.\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, + on ? "enabled" : "disabled"); return asynSuccess; } @@ -1066,9 +1201,9 @@ asynStatus turboPmacAxis::enable(bool on) { // Enable / disable the axis if it is not moving snprintf(command, sizeof(command), "M%2.2d14=%d", axisNo_, on); asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW, - "%s => line %d:\n%s axis %d on controller %s\n", - __PRETTY_FUNCTION__, __LINE__, on ? "Enable" : "Disable", axisNo_, - pC_->portName); + "Controller \"%s\", axis %d => %s, line %d\n%s axis\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, + on ? "Enable" : "Disable"); if (on == 0) { pl_status = setStringParam(pC_->motorMessageText_, "Disabling ..."); } else { @@ -1076,7 +1211,8 @@ asynStatus turboPmacAxis::enable(bool on) { } if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", - __PRETTY_FUNCTION__, __LINE__); + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } rw_status = pC_->writeRead(axisNo_, command, response, 0); if (rw_status != asynSuccess) { @@ -1112,10 +1248,10 @@ asynStatus turboPmacAxis::enable(bool on) { // Failed to change axis status within timeout_enable_disable => Send a // corresponding message asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW, - "%s => line %d:\nFailed to %s axis %d on controller %s within %d " - "seconds\n", - __PRETTY_FUNCTION__, __LINE__, on ? "enable" : "disable", axisNo_, - pC_->portName, timeout_enable_disable); + "Controller \"%s\", axis %d => %s, line %d\nFailed to %s axis " + "within %d seconds\n", + pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, + on ? "enable" : "disable", timeout_enable_disable); // Output message to user snprintf(command, sizeof(command), "Failed to %s within %d seconds", @@ -1123,7 +1259,104 @@ asynStatus turboPmacAxis::enable(bool on) { pl_status = setStringParam(pC_->motorMessageText_, "Enabling ..."); if (pl_status != asynSuccess) { return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", - __PRETTY_FUNCTION__, __LINE__); + axisNo_, __PRETTY_FUNCTION__, + __LINE__); } return asynError; } + +/*************************************************************************************/ +/** The following functions are C-wrappers, and can be called directly from + * iocsh */ + +extern "C" { + +/* +C wrapper for the axis constructor. Please refer to the turboPmacAxis +constructor documentation. The controller is read from the portName. +*/ +asynStatus turboPmacCreateAxis(const char *portName, int axis) { + + /* + findAsynPortDriver is a asyn library FFI function which uses the C ABI. + Therefore it returns a void pointer instead of e.g. a pointer to a + superclass of the controller such as asynPortDriver. Type-safe upcasting + via dynamic_cast is therefore not possible directly. However, we do know + that the void pointer is either a pointer to asynPortDriver (if a driver + with the specified name exists) or a nullptr. Therefore, we first do a + nullptr check, then a cast to asynPortDriver and lastly a (typesafe) + dynamic_upcast to Controller + https://stackoverflow.com/questions/70906749/is-there-a-safe-way-to-cast-void-to-class-pointer-in-c + */ + void *ptr = findAsynPortDriver(portName); + if (ptr == nullptr) { + /* + We can't use asynPrint here since this macro would require us + to get a lowLevelPortUser_ from a pointer to an asynPortDriver. + However, the given pointer is a nullptr and therefore doesn't + have a lowLevelPortUser_! printf is an EPICS alternative which + works w/o that, but doesn't offer the comfort provided + by the asynTrace-facility + */ + errlogPrintf("Controller \"%s\" => %s, line %d\nPort not found.", + portName, __PRETTY_FUNCTION__, __LINE__); + return asynError; + } + // Unsafe cast of the pointer to an asynPortDriver + asynPortDriver *apd = (asynPortDriver *)(ptr); + + // Safe downcast + turboPmacController *pC = dynamic_cast(apd); + if (pC == nullptr) { + errlogPrintf("Controller \"%s\" => %s, line %d\nController " + "is not a turboPmacController.", + portName, __PRETTY_FUNCTION__, __LINE__); + return asynError; + } + + // Prevent manipulation of the controller from other threads while we + // create the new axis. + pC->lock(); + + /* + We create a new instance of the axis, using the "new" keyword to + allocate it on the heap while avoiding RAII. + https://github.com/epics-modules/motor/blob/master/motorApp/MotorSrc/asynMotorController.cpp + https://github.com/epics-modules/asyn/blob/master/asyn/asynPortDriver/asynPortDriver.cpp + + The created object is registered in EPICS in its constructor and can safely + be "leaked" here. + */ +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" +#pragma GCC diagnostic ignored "-Wunused-variable" + turboPmacAxis *pAxis = new turboPmacAxis(pC, axis); + + // Allow manipulation of the controller again + pC->unlock(); + return asynSuccess; +} + +/* +Same procedure as for the CreateController function, but for the axis +itself. +*/ +static const iocshArg CreateAxisArg0 = {"Controller name (e.g. mcu1)", + iocshArgString}; +static const iocshArg CreateAxisArg1 = {"Axis number", iocshArgInt}; +static const iocshArg *const CreateAxisArgs[] = {&CreateAxisArg0, + &CreateAxisArg1}; +static const iocshFuncDef configTurboPmacCreateAxis = {"turboPmacAxis", 2, + CreateAxisArgs}; +static void configTurboPmacCreateAxisCallFunc(const iocshArgBuf *args) { + turboPmacCreateAxis(args[0].sval, args[1].ival); +} + +// This function is made known to EPICS in turboPmac.dbd and is called by EPICS +// in order to register both functions in the IOC shell +static void turboPmacAxisRegister(void) { + iocshRegister(&configTurboPmacCreateAxis, + configTurboPmacCreateAxisCallFunc); +} +epicsExportRegistrar(turboPmacAxisRegister); + +} // extern "C" \ No newline at end of file diff --git a/src/turboPmacAxis.h b/src/turboPmacAxis.h index 594e4fe..96da1c1 100644 --- a/src/turboPmacAxis.h +++ b/src/turboPmacAxis.h @@ -69,7 +69,7 @@ class turboPmacAxis : public sinqAxis { double max_velocity, double acceleration); /** - * @brief Implementation of the `atFirstPoll` function from sinqAxis. + * @brief Readout of some values from the controller at IOC startup * * The following steps are performed: * - Read out the motor status, motor position, velocity and acceleration @@ -78,7 +78,7 @@ class turboPmacAxis : public sinqAxis { * * @return asynStatus */ - asynStatus atFirstPoll(); + asynStatus init(); /** * @brief Enable / disable the axis. @@ -106,7 +106,6 @@ class turboPmacAxis : public sinqAxis { protected: turboPmacController *pC_; - bool initial_poll_; bool waitForHandshake_; time_t timeAtHandshake_; diff --git a/src/turboPmacController.cpp b/src/turboPmacController.cpp index 88e9ec0..07b5466 100644 --- a/src/turboPmacController.cpp +++ b/src/turboPmacController.cpp @@ -4,6 +4,7 @@ #include "turboPmacAxis.h" #include #include +#include #include #include #include @@ -63,6 +64,10 @@ turboPmacController::turboPmacController(const char *portName, lowLevelPortUser_ = nullptr; comTimeout_ = comTimeout; + // Maximum allowed number of subsequent timeouts before the user is + // informed. + maxSubsequentTimeouts_ = 10; + // =========================================================================; /* @@ -71,10 +76,9 @@ turboPmacController::turboPmacController(const char *portName, */ pasynOctetSyncIO->connect(ipPortConfigName, 0, &lowLevelPortUser_, NULL); if (status != asynSuccess || lowLevelPortUser_ == nullptr) { - errlogPrintf( - "%s => line %d:\nFATAL ERROR (cannot connect to MCU controller).\n" - "Terminating IOC", - __PRETTY_FUNCTION__, __LINE__); + errlogPrintf("Controller \"%s\" => %s, line %d\nFATAL ERROR " + "(cannot connect to MCU controller).\nTerminating IOC", + portName, __PRETTY_FUNCTION__, __LINE__); exit(-1); } @@ -85,18 +89,20 @@ turboPmacController::turboPmacController(const char *portName, &rereadEncoderPosition_); if (status != asynSuccess) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, - "%s => line %d:\nFATAL ERROR (creating a parameter failed " - "with %s).\nTerminating IOC", - __PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status)); + "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("READ_CONFIG", asynParamInt32, &readConfig_); if (status != asynSuccess) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, - "%s => line %d:\nFATAL ERROR (creating a parameter failed " - "with %s).\nTerminating IOC", - __PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status)); + "Controller \"%s\" => %s, line %d\nFATAL ERROR (creating a " + "parameter failed with %s).\nTerminating IOC", + portName, __PRETTY_FUNCTION__, __LINE__, + stringifyAsynStatus(status)); exit(-1); } @@ -113,20 +119,22 @@ turboPmacController::turboPmacController(const char *portName, lowLevelPortUser_, message_from_device, strlen(message_from_device)); if (status != asynSuccess) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, - "%s => line %d:\nFATAL ERROR (setting input EOS failed " - "with %s).\nTerminating IOC", - __PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status)); + "Controller \"%s\" => %s, line %d\nFATAL ERROR " + "(setting input EOS failed with %s).\nTerminating IOC", + portName, __PRETTY_FUNCTION__, __LINE__, + stringifyAsynStatus(status)); pasynOctetSyncIO->disconnect(lowLevelPortUser_); exit(-1); } status = callParamCallbacks(); if (status != asynSuccess) { - asynPrint( - this->pasynUserSelf, ASYN_TRACE_ERROR, - "%s => line %d:\nFATAL ERROR (executing ParamLib callbacks failed " - "with %s).\nTerminating IOC", - __PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status)); + asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, + "Controller \"%s\" => %s, line %d\nFATAL ERROR " + "(executing ParamLib callbacks failed " + "with %s).\nTerminating IOC", + portName, __PRETTY_FUNCTION__, __LINE__, + stringifyAsynStatus(status)); pasynOctetSyncIO->disconnect(lowLevelPortUser_); exit(-1); } @@ -165,8 +173,9 @@ turboPmacAxis *turboPmacController::castToAxis(asynMotorAxis *asynAxis) { turboPmacAxis *axis = dynamic_cast(asynAxis); if (axis == nullptr) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, - "%s => line %d:\nAxis %d is not an instance of turboPmacAxis", - __PRETTY_FUNCTION__, __LINE__, axis->axisNo_); + "Controller \"%s\", axis %d => %s, line %d\nAxis is not " + "an instance of turboPmacAxis", + portName, axis->axisNo_, __PRETTY_FUNCTION__, __LINE__); } return axis; } @@ -177,7 +186,8 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command, // Definition of local variables. asynStatus status = asynSuccess; - asynStatus pl_status = asynSuccess; + asynStatus paramLibStatus = asynSuccess; + asynStatus timeoutStatus = asynSuccess; char fullCommand[MAXBUF_] = {0}; char drvMessageText[MAXBUF_] = {0}; char modResponse[MAXBUF_] = {0}; @@ -226,6 +236,11 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command, value [Actual message] It is not necessary to append a terminator, since this protocol encodes the message length at the beginning. See Turbo PMAC User Manual, page 418 in VR_PMAC_GETRESPONSE + x0D (ASCII value of carriage return) -> The controller needs a carriage + return at the end of a "send" command (a command were we transmit data via + =). For "request" commands (e.g. read status or position), this is not + necessary, but it doesn't hurt either, therefore we always add a carriage + return. The message has to be build manually into the buffer fullCommand, since it contains NULL terminators in its middle, therefore the string manipulation @@ -242,46 +257,25 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command, // The size of size_t is platform dependant (pointers-sized), while htons // needs an unsigned int. The byte order is then converted from host to - // network order. - u_int16_t len = htons(static_cast(commandLength)); + // network order. The offset "+1" is for the carriage return. + u_int16_t len = htons(static_cast(commandLength + 1)); // Split up into the upper and the lower byte fullCommand[7] = (char)(len >> 8); // Shift the 8 higher bits to the right fullCommand[8] = (char)(len & 0xFF); // Mask the higher bits // Write the actual command behind the protocol - for (int i = 0; i < commandLength; i++) { + for (size_t i = 0; i < commandLength; i++) { fullCommand[i + offset] = command[i]; } + fullCommand[offset + commandLength] = '\x0D'; + + // +1 for the carriage return. + const size_t fullComandLength = offset + commandLength + 1; asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, - "%s => line %d:\nSending command %s", __PRETTY_FUNCTION__, - __LINE__, fullCommand); - - // TEST CODE - /* - Check if there is data on the MCU which is waiting to be read - TBD: Flush if there is something? - */ - u_int16_t val = htons(2); - char readReadyCommand[MAXBUF_] = {0}; - char readReadyResponse[MAXBUF_] = {0}; - readReadyCommand[0] = '\xC0'; - readReadyCommand[1] = '\xC2'; - readReadyCommand[4] = (char)(val >> 8); - readReadyCommand[5] = (char)(val & 0xFF); - status = pasynOctetSyncIO->write(lowLevelPortUser_, readReadyCommand, 6, - comTimeout_, &nbytesOut); - - status = - pasynOctetSyncIO->read(lowLevelPortUser_, readReadyResponse, MAXBUF_, - comTimeout_, &nbytesIn, &eomReason); - - if (readReadyResponse[1] != 0) { - asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, - "%s => line %d:\nBytes: %x %x\n", __PRETTY_FUNCTION__, - __LINE__, readReadyResponse[0], readReadyResponse[1]); - } + "Controller \"%s\", axis %d => %s, line %d\nSending command %s", + portName, axisNo, __PRETTY_FUNCTION__, __LINE__, fullCommand); /* We use separated write and read commands here, not the combined writeRead @@ -291,60 +285,105 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command, The flush itself reads repeatedly from the MCU until no messages are there anymore. (The Diamond Light Source driver first send a PMAC flush command and then does the same as the asyn flush). We don't want this behaviour. - (https://www.slac.stanford.edu/grp/lcls/controls/global/doc/epics-modules/R3-14-12/asyn/asyn-R4-18-lcls2/asyn/interfaces/asynOctetBase.c): + (https://www.slac.stanford.edu/grp/lcls/controls/global/doc/epics-modules/R3-14-12/asyn/asyn-R4-18-lcls2/asyn/interfaces/asynOctetBase.c) + + If a timeout occurs during writing or reading, inform the user that we're + trying to reconnect. If the problem persists, ask them to call the support */ - // Send out the command status = pasynOctetSyncIO->write(lowLevelPortUser_, fullCommand, - commandLength + offset, comTimeout_, - &nbytesOut); - if (status != asynSuccess) { + fullComandLength, comTimeout_, &nbytesOut); + + if (status == asynTimeout) { + asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, - "%s => line %d:\nError %s while writing to the controller\n", - __PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status)); - pl_status = setStringParam( - motorMessageText_, "Communication timeout between IOC and " - "motor controller. Please call the support."); - if (pl_status != asynSuccess) { - return paramLibAccessFailed(pl_status, "motorMessageText_", - __PRETTY_FUNCTION__, __LINE__); + "Controller \"%s\", axis %d => %s, line %d\nTimeout while " + "writing to the MCU\n", + portName, axisNo, __PRETTY_FUNCTION__, __LINE__); + + timeoutStatus = checkComTimeoutWatchdog(axisNo, drvMessageText, + sizeof(drvMessageText)); + + int timeoutCounter = 0; + while (1) { + checkMaxSubsequentTimeouts(timeoutCounter, axis); + timeoutCounter += 1; + + status = pasynOctetSyncIO->write(lowLevelPortUser_, fullCommand, + fullComandLength, comTimeout_, + &nbytesOut); + if (status != asynTimeout) { + asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, + "Controller \"%s\", axis %d => %s, line " + "%d\nReconnected after write timeout\n", + portName, axisNo, __PRETTY_FUNCTION__, __LINE__); + break; + } } + } else if (status != asynSuccess) { + asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, + "Controller \"%s\", axis %d => %s, line %d\nError %s while " + "writing to the controller\n", + portName, axisNo, __PRETTY_FUNCTION__, __LINE__, + stringifyAsynStatus(status)); } // Read the response from the MCU buffer status = pasynOctetSyncIO->read(lowLevelPortUser_, response, MAXBUF_, comTimeout_, &nbytesIn, &eomReason); - if (status != asynSuccess) { - asynPrint( - this->pasynUserSelf, ASYN_TRACE_ERROR, - "%s => line %d:\nError %s while reading from the controller\n ", - __PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status)); - pl_status = setStringParam( - motorMessageText_, "Communication timeout between IOC and " - "motor controller. Please call the support."); - if (pl_status != asynSuccess) { - return paramLibAccessFailed(pl_status, "motorMessageText_", - __PRETTY_FUNCTION__, __LINE__); + if (status == asynTimeout) { + + asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, + "Controller \"%s\", axis %d => %s, line %d\nTimeout while " + "reading from the MCU\n", + portName, axisNo, __PRETTY_FUNCTION__, __LINE__); + + // Add this event to the back of the timeout event counter + timeoutStatus = checkComTimeoutWatchdog(axisNo, drvMessageText, + sizeof(drvMessageText)); + + int timeoutCounter = 0; + while (1) { + checkMaxSubsequentTimeouts(timeoutCounter, axis); + timeoutCounter += 1; + + status = + pasynOctetSyncIO->read(lowLevelPortUser_, response, MAXBUF_, + comTimeout_, &nbytesIn, &eomReason); + if (status != asynTimeout) { + asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, + "Controller \"%s\", axis %d => %s, line " + "%d\nReconnected after read timeout\n", + portName, axisNo, __PRETTY_FUNCTION__, __LINE__); + break; + } } + } else if (status != asynSuccess) { + asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, + "Controller \"%s\", axis %d => %s, line %d\nError %s while " + "reading from the controller\n", + portName, axisNo, __PRETTY_FUNCTION__, __LINE__, + stringifyAsynStatus(status)); } - // // TEST CODE - // if (response == lastResponse) { - // asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, - // "%s => line %d:\nBytes: %x %x\n", __PRETTY_FUNCTION__, - // __LINE__, readReadyResponse[0], readReadyResponse[1]); - // } else { - // for (int i = 0; i < MAXBUF_; i++) { - // lastResponse[i] = response[i]; - // } - // } + if (timeoutStatus == asynError) { + status = asynError; + } - // TBD: The message should only ever terminate due to reason 2 -> Should we - // indicate an error otherwise? - asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, - "%s => line %d:\nMessage terminated due to reason %i\n", - __PRETTY_FUNCTION__, __LINE__, eomReason); + // The message should only ever terminate due to reason 2 + if (eomReason != 2) { + status = asynError; + + snprintf(drvMessageText, sizeof(drvMessageText), + "Terminated message due to reason %d (should be 2).", + eomReason); + + asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, + "Controller \"%s\", axis %d => %s, line %d\nMessage " + "terminated due to reason %i\n", + portName, axisNo, __PRETTY_FUNCTION__, __LINE__, eomReason); + } /* Calculate the number of received responses by counting the number of @@ -358,23 +397,21 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command, if (numExpectedResponses != numReceivedResponses) { adjustResponseForPrint(modResponse, response, MAXBUF_); asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, - "%s => line %d:\nUnexpected response '%s' (carriage " - "returns are replaced with spaces) for command %s\n", - __PRETTY_FUNCTION__, __LINE__, modResponse, command); + "Controller \"%s\", axis %d => %s, line %d\nUnexpected " + "response '%s' (carriage returns are replaced with spaces) " + "for command %s\n", + portName, axisNo, __PRETTY_FUNCTION__, __LINE__, modResponse, + command); snprintf(drvMessageText, sizeof(drvMessageText), "Received unexpected response '%s' (carriage returns " "are replaced with spaces) for command %s. " "Please call the support", modResponse, command); - pl_status = setStringParam(motorMessageText_, drvMessageText); - if (pl_status != asynSuccess) { - return paramLibAccessFailed(pl_status, "motorMessageText_", - __PRETTY_FUNCTION__, __LINE__); - } status = asynError; } - // Create custom error messages for different failure modes + // Create custom error messages for different failure modes, if no error + // message has been set yet if (strlen(drvMessageText) == 0) { switch (status) { case asynSuccess: @@ -402,43 +439,48 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command, if (status == asynSuccess) { asynPrint(lowLevelPortUser_, ASYN_TRACEIO_DRIVER, - "%s => line %d:\nDevice response: %s (_ are " - "carriage returns)\n", - __PRETTY_FUNCTION__, __LINE__, modResponse); - pl_status = axis->setIntegerParam(this->motorStatusCommsError_, 0); + "Controller \"%s\", axis %d => %s, line %d\nDevice " + "response: %s (carriage returns are replaced with spaces)\n", + portName, axisNo, __PRETTY_FUNCTION__, __LINE__, modResponse); + paramLibStatus = axis->setIntegerParam(this->motorStatusCommsError_, 0); } else { asynPrint(lowLevelPortUser_, ASYN_TRACE_ERROR, - "%s => line %d:\nCommunication failed for command %s (%s)\n", - __PRETTY_FUNCTION__, __LINE__, fullCommand, + "Controller \"%s\", axis %d => %s, line %d\nCommunication " + "failed for command %s (%s)\n", + portName, axisNo, __PRETTY_FUNCTION__, __LINE__, fullCommand, stringifyAsynStatus(status)); // Check if the axis already is in an error communication mode. If it is // not, upstream the error. This is done to avoid "flooding" the user // with different error messages if more than one error ocurred before // an error-free communication - pl_status = + paramLibStatus = getIntegerParam(axisNo, motorStatusProblem_, &motorStatusProblem); - if (pl_status != asynSuccess) { - return paramLibAccessFailed(pl_status, "motorStatusProblem_", - __PRETTY_FUNCTION__, __LINE__); + if (paramLibStatus != asynSuccess) { + return paramLibAccessFailed(paramLibStatus, "motorStatusProblem_", + axisNo, __PRETTY_FUNCTION__, __LINE__); } if (motorStatusProblem == 0) { - pl_status = axis->setStringParam(motorMessageText_, drvMessageText); - if (pl_status != asynSuccess) { - return paramLibAccessFailed(pl_status, "motorMessageText_", + paramLibStatus = + axis->setStringParam(motorMessageText_, drvMessageText); + if (paramLibStatus != asynSuccess) { + return paramLibAccessFailed(paramLibStatus, "motorMessageText_", + axisNo, __PRETTY_FUNCTION__, + __LINE__); + } + + paramLibStatus = axis->setIntegerParam(motorStatusProblem_, 1); + if (paramLibStatus != asynSuccess) { + return paramLibAccessFailed(paramLibStatus, + "motorStatusProblem", axisNo, __PRETTY_FUNCTION__, __LINE__); } - pl_status = axis->setIntegerParam(motorStatusProblem_, 1); - if (pl_status != asynSuccess) { - return paramLibAccessFailed(pl_status, "motorStatusProblem", - __PRETTY_FUNCTION__, __LINE__); - } - - pl_status = axis->setIntegerParam(motorStatusProblem_, 1); - if (pl_status != asynSuccess) { - return paramLibAccessFailed(pl_status, "motorStatusCommsError_", + paramLibStatus = axis->setIntegerParam(motorStatusProblem_, 1); + if (paramLibStatus != asynSuccess) { + return paramLibAccessFailed(paramLibStatus, + "motorStatusCommsError_", axisNo, __PRETTY_FUNCTION__, __LINE__); } } @@ -462,7 +504,7 @@ asynStatus turboPmacController::writeInt32(asynUser *pasynUser, if (function == rereadEncoderPosition_) { return axis->rereadEncoder(); } else if (function == readConfig_) { - return axis->atFirstPoll(); + return axis->init(); } else { return sinqController::writeInt32(pasynUser, value); } @@ -519,80 +561,6 @@ asynStatus turboPmacCreateController(const char *portName, return asynSuccess; } -/* -C wrapper for the axis constructor. Please refer to the turboPmacAxis -constructor documentation. The controller is read from the portName. -*/ -asynStatus turboPmacCreateAxis(const char *portName, int axis) { - turboPmacAxis *pAxis; - - /* - findAsynPortDriver is a asyn library FFI function which uses the C ABI. - Therefore it returns a void pointer instead of e.g. a pointer to a - superclass of the controller such as asynPortDriver. Type-safe upcasting - via dynamic_cast is therefore not possible directly. However, we do know - that the void pointer is either a pointer to asynPortDriver (if a driver - with the specified name exists) or a nullptr. Therefore, we first do a - nullptr check, then a cast to asynPortDriver and lastly a (typesafe) - dynamic_upcast to Controller - https://stackoverflow.com/questions/70906749/is-there-a-safe-way-to-cast-void-to-class-pointer-in-c - */ - void *ptr = findAsynPortDriver(portName); - if (ptr == nullptr) { - /* - We can't use asynPrint here since this macro would require us - to get a lowLevelPortUser_ from a pointer to an asynPortDriver. - However, the given pointer is a nullptr and therefore doesn't - have a lowLevelPortUser_! printf is an EPICS alternative which - works w/o that, but doesn't offer the comfort provided - by the asynTrace-facility - */ - errlogPrintf("%s => line %d:\nPort %s not found.", __PRETTY_FUNCTION__, - __LINE__, portName); - return asynError; - } - // Unsafe cast of the pointer to an asynPortDriver - asynPortDriver *apd = (asynPortDriver *)(ptr); - - // Safe downcast - turboPmacController *pC = dynamic_cast(apd); - if (pC == nullptr) { - errlogPrintf("%s => line %d:\ncontroller on port %s is not a " - "turboPmacController.", - __PRETTY_FUNCTION__, __LINE__, portName); - return asynError; - } - - // Prevent manipulation of the controller from other threads while we - // create the new axis. - pC->lock(); - - /* - We create a new instance of the axis, using the "new" keyword to - allocate it on the heap while avoiding RAII. - https://github.com/epics-modules/motor/blob/master/motorApp/MotorSrc/asynMotorController.cpp - https://github.com/epics-modules/asyn/blob/master/asyn/asynPortDriver/asynPortDriver.cpp - - The created object is registered in EPICS in its constructor and can safely - be "leaked" here. - */ -#pragma GCC diagnostic ignored "-Wunused-but-set-variable" -#pragma GCC diagnostic ignored "-Wunused-variable" - pAxis = new turboPmacAxis(pC, axis); - - // Allow manipulation of the controller again - pC->unlock(); - return asynSuccess; -} - -/* -This is boilerplate code which is used to make the FFI functions -CreateController and CreateAxis "known" to the IOC shell (iocsh). -*/ - -#ifdef vxWorks -#else - /* Define name and type of the arguments for the CreateController function in the iocsh. This is done by creating structs with the argument names and @@ -616,36 +584,17 @@ static const iocshArg *const CreateControllerArgs[] = { &CreateControllerArg3, &CreateControllerArg4, &CreateControllerArg5}; static const iocshFuncDef configturboPmacCreateController = { "turboPmacController", 6, CreateControllerArgs}; -static void configturboPmacCreateControllerCallFunc(const iocshArgBuf *args) { +static void configTurboPmacCreateControllerCallFunc(const iocshArgBuf *args) { turboPmacCreateController(args[0].sval, args[1].sval, args[2].ival, args[3].dval, args[4].dval, args[5].dval); } -/* -Same procedure as for the CreateController function, but for the axis -itself. -*/ -static const iocshArg CreateAxisArg0 = {"Controller name (e.g. mcu1)", - iocshArgString}; -static const iocshArg CreateAxisArg1 = {"Axis number", iocshArgInt}; -static const iocshArg *const CreateAxisArgs[] = {&CreateAxisArg0, - &CreateAxisArg1}; -static const iocshFuncDef configturboPmacCreateAxis = {"turboPmacAxis", 2, - CreateAxisArgs}; -static void configturboPmacCreateAxisCallFunc(const iocshArgBuf *args) { - turboPmacCreateAxis(args[0].sval, args[1].ival); -} - // This function is made known to EPICS in turboPmac.dbd and is called by EPICS // in order to register both functions in the IOC shell -static void turboPmacRegister(void) { +static void turboPmacControllerRegister(void) { iocshRegister(&configturboPmacCreateController, - configturboPmacCreateControllerCallFunc); - iocshRegister(&configturboPmacCreateAxis, - configturboPmacCreateAxisCallFunc); + configTurboPmacCreateControllerCallFunc); } -epicsExportRegistrar(turboPmacRegister); - -#endif +epicsExportRegistrar(turboPmacControllerRegister); } // extern "C" diff --git a/src/turboPmacController.h b/src/turboPmacController.h index 9b8575b..28b6524 100644 --- a/src/turboPmacController.h +++ b/src/turboPmacController.h @@ -1,16 +1,16 @@ /******************************************** * turboPmacController.h * - * PMAC V3 controller driver based on the asynMotorController class + * Turbo PMAC controller driver based on the asynMotorController class * * Stefan Mathis, September 2024 ********************************************/ #ifndef turboPmacController_H #define turboPmacController_H -#include "turboPmacAxis.h" #include "sinqAxis.h" #include "sinqController.h" +#include "turboPmacAxis.h" class turboPmacController : public sinqController { @@ -28,14 +28,15 @@ class turboPmacController : public sinqController { time (in seconds) has passed, then it declares a timeout. */ turboPmacController(const char *portName, const char *ipPortConfigName, - int numAxes, double movingPollPeriod, - double idlePollPeriod, double comTimeout); + int numAxes, double movingPollPeriod, + double idlePollPeriod, double comTimeout); /** * @brief Get the axis object * * @param pasynUser Specify the axis via the asynUser - * @return turboPmacAxis* If no axis could be found, this is a nullptr + * @return turboPmacAxis* If no axis could be found, this is a + * nullptr */ turboPmacAxis *getAxis(asynUser *pasynUser); @@ -43,7 +44,8 @@ class turboPmacController : public sinqController { * @brief Get the axis object * * @param axisNo Specify the axis via its index - * @return turboPmacAxis* If no axis could be found, this is a nullptr + * @return turboPmacAxis* If no axis could be found, this is a + * nullptr */ turboPmacAxis *getAxis(int axisNo); @@ -81,8 +83,8 @@ class turboPmacController : public sinqController { int numExpectedResponses); /** - * @brief Save cast of the given asynAxis pointer to a turboPmacAxis pointer. - * If the cast fails, this function returns a nullptr. + * @brief Save cast of the given asynAxis pointer to a turboPmacAxis + * pointer. If the cast fails, this function returns a nullptr. * * @param asynAxis * @return turboPmacAxis* @@ -119,7 +121,7 @@ class turboPmacController : public sinqController { static const uint32_t MAXBUF_ = 200; /* - Stores the constructor input comTimeout + Timeout for the communication process in seconds */ double comTimeout_; @@ -133,6 +135,7 @@ class turboPmacController : public sinqController { friend class turboPmacAxis; }; -#define NUM_turboPmac_DRIVER_PARAMS (&LAST_turboPmac_PARAM - &FIRST_turboPmac_PARAM + 1) +#define NUM_turboPmac_DRIVER_PARAMS \ + (&LAST_turboPmac_PARAM - &FIRST_turboPmac_PARAM + 1) #endif /* turboPmacController_H */