diff --git a/Makefile.RHEL8 b/Makefile.RHEL8 index bd514aa..0d24e3d 100644 --- a/Makefile.RHEL8 +++ b/Makefile.RHEL8 @@ -15,7 +15,7 @@ REQUIRED+=asynMotor # using a test version #scaler_VERSION=2024 -LIBVERSION=2024 +LIBVERSION=2024-dev TEMPLATES += sinqEPICSApp/Db/dimetix.db TEMPLATES += sinqEPICSApp/Db/slsvme.db @@ -35,6 +35,5 @@ SOURCES += sinqEPICSApp/src/EuroMoveDriver.cpp SOURCES += sinqEPICSApp/src/pmacAsynIPPort.c SOURCES += sinqEPICSApp/src/pmacAxis.cpp SOURCES += sinqEPICSApp/src/pmacController.cpp -SOURCES += sinqEPICSApp/src/drvAsynMasterMACSPort.c SOURCES += sinqEPICSApp/src/MasterMACSDriver.cpp # MISCS would be the place to keep the stream device template files diff --git a/sinqEPICSApp/src/pmacAxis.cpp b/sinqEPICSApp/src/pmacAxis.cpp index 79cd046..d83248a 100644 --- a/sinqEPICSApp/src/pmacAxis.cpp +++ b/sinqEPICSApp/src/pmacAxis.cpp @@ -32,6 +32,10 @@ * * Mark Koennecke, June 2023 * + * Added driver for GirderAxis + * + * Stefan Mathis, July 2024 + * ********************************************/ #include @@ -1479,4 +1483,275 @@ asynStatus AmorDetectorAxis::poll(bool *moving) return status; } +/*-------------------------------------------------------------------------------*/ +GirderAxis::GirderAxis(pmacController *pController, int axisNo) + : pmacV3Axis(pController, axisNo){ + + // GirderAxis expects a pmacV3Controller. Therefore it is checked whether the pointer pController can be cast + // to this object type. If this is not possible, the user made an error in the configuration files. This is documented + // in a corresponding error; after that, an exception is thrown to avoid returning an "illegal" instance of GirderAxis. + pmacV3Controller* pV3Controller = dynamic_cast(pController); + if (pV3Controller == nullptr) { + errlogPrintf("A GirderAxis instance needs a pmacV3Controller. Please check the configuration files."); + throw std::invalid_argument("A GirderAxis instance needs a pmacV3Controller. Please check the configuration files."); + } + + // Initial values for member variables + next_poll = -1; + previous_position_ = 0.0; + previous_direction_ = 0; + status6Time = 0; + statusPos = 0.0; + homing = 0; + axisErrorCount = 0; +}; + +/*-------------------------------------------------------------------------------*/ +asynStatus GirderAxis::move(double position, int relative, double min_velocity, double max_velocity, double acceleration) { + + // If the axis is not enabled, do nothing + if(!IsEnable) { + updateMsgTxtFromDriver("Error: axis disabled"); + return asynError; + } + + // ======================================= + // Local variable declaration + + asynStatus status = asynError; + char command[pC_->PMAC_MAXBUF_] = {0}; + char response[pC_->PMAC_MAXBUF_] = {0}; + double realPosition = 0; + + // ======================================= + + updateMsgTxtFromDriver(""); + static const char *functionName = "GirderAxis::move"; + pC_->debugFlow(functionName); + + if (relative) { + realPosition = previous_position_ + position / MULT; + } else { + realPosition = position / MULT; + } + startTime = time(NULL); + status6Time = 0; + starting = 1; + + // Set target position + snprintf(command, sizeof(command), "Q251=%f", realPosition); + status = pC_->lowLevelWriteRead(axisNo_, command, response); + if (status == asynError) { + updateMsgTxtFromDriver("Error: Could not set target position"); + return asynError; + } + + // Start motion + snprintf(command, sizeof(command), "P150=1"); + status = pC_->lowLevelWriteRead(axisNo_, command, response); + if (status == asynError) { + updateMsgTxtFromDriver("Error: Could not start motion"); + return asynError; + } + + next_poll = -1; + return status; +} + +asynStatus GirderAxis::stop(double acceleration) { + + // ======================================= + // Local variable declaration + + asynStatus status = asynSuccess; + static const char *functionName = "GirderAxis::stop"; + bool moving = false; + char command[pC_->PMAC_MAXBUF_] = {0}; + char response[pC_->PMAC_MAXBUF_] = {0}; + + // ======================================= + + pC_->debugFlow(functionName); + this->poll(&moving); + + if(moving) { + // only send a stop when actually moving + snprintf(command, sizeof(command), "P150=8"); + status = pC_->lowLevelWriteRead(axisNo_, command, response); + } + return status; +} + +asynStatus GirderAxis::poll(bool *moving) { + + // ======================================= + // Local variable declaration + + asynStatus status = asynSuccess; + static const char *functionName = "GirderAxis::poll"; + char command[pC_->PMAC_MAXBUF_] = {0}; + char response[pC_->PMAC_MAXBUF_] = {0}; + char message[132], *axMessage; + double position = 0; + int moving_to_position = 0, nvals = 0, axisProblemFlag = 0, axStat = 0, axError = 0, isEnabled = 1; + asynStatus st; + + // ======================================= + + // Check for girder axis specific errors + snprintf(command, sizeof(command), "P159"); + status = pC_->lowLevelWriteRead(axisNo_, command, response); + sscanf(response, "P159=%d", &axError); + switch(axError) { + case 1: + snprintf(message, sizeof(message), "PMAC: %s on %d", + "Axis not switched on", axisNo_); + updateMsgTxtFromDriver(message); + asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "%s\n", message); + axisProblemFlag = 1; + isEnabled = 0; + break; + case 2: + snprintf(message, sizeof(message), "PMAC: %s on %d", + "Axis not ready for new command", axisNo_); + updateMsgTxtFromDriver(message); + asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "%s\n", message); + axisProblemFlag = 1; + break; + case 3: + snprintf(message, sizeof(message), "PMAC: %s on %d", + "Axis 1 ERROR during motion", axisNo_); + updateMsgTxtFromDriver(message); + asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "%s\n", message); + axisProblemFlag = 1; + break; + case 4: + snprintf(message, sizeof(message), "PMAC: %s on %d", + "Axis 2 ERROR during motion", axisNo_); + updateMsgTxtFromDriver(message); + asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "%s\n", message); + axisProblemFlag = 1; + break; + } + st = setIntegerParam(pC_->motorStatusProblem_, axisProblemFlag); + status = status > st ? status : st; + + // Downcast the pointer pmacController to pmacV3Controller in a typesafe manner + pmacV3Controller* p3C_ = dynamic_cast(pC_); + if (p3C_ == nullptr) { + errlogPrintf("A GirderAxis instance needs a pmacV3Controller. Please check the configuration files."); + throw std::invalid_argument("A GirderAxis instance needs a pmacV3Controller. Please check the configuration files."); + } + setIntegerParam(p3C_->axisEnabled_, isEnabled); + + st = setIntegerParam(p3C_->enableAxis_, isEnabled); + status = status > st ? status : st; + + int direction = 0; + + /* + In GirderAxis::move, the user input is scaled by /MULT. Hence, the output of + Qxx10 needs to be scaled by *MULT + */ + st = setDoubleParam(pC_->motorPosition_, position * MULT); + status = status > st ? status : st; + st = setDoubleParam(pC_->motorEncoderPosition_, position * MULT); + status = status > st ? status : st; + + // Calculate the current (or last) movement direction + if ((position - previous_position_) > 0) { + direction = 1; + } else if (position - previous_position_ == 0.0) { + direction = previous_direction_; + } else { + direction = 0; + } + st = setIntegerParam(pC_->motorStatusDirection_, direction); + status = status > st ? status : st; + + // Store the position which was read out from the hardware together with the calculated direction for the next poll + previous_position_ = position; + previous_direction_ = direction; + + // Is the axis currently moving? + snprintf(command, sizeof(command), "P154"); + status = pC_->lowLevelWriteRead(axisNo_, command, response); + sscanf(response, "P154=%d", &moving_to_position); + + /* + This code tests whether the axis is too long in status 5 or 6. + If the axis is in status 5 or 6, a time counter (status6Time) is started and updated during subsequent polls. + If status6Time exceeds the estimated arrival time of 120 seconds, a corresponding error is returned. + */ + int EstimatedTimeOfArrival = 120; // seconds + if (axStat == 5 || axStat == 6) { + if (status6Time == 0) { + status6Time = time(nullptr); + statusPos = position; + } else { + if (time(nullptr) > status6Time + EstimatedTimeOfArrival) { + /* trigger error only when not moving */ + if (abs(position - statusPos) < .1) { + moving_to_position = 0; + errlogPrintf( + "Axis %d stayed in status 5 or 6 for more than %d seconds BROKEN\n", + axisNo_, EstimatedTimeOfArrival); + updateMsgTxtFromDriver("Axis stayed in status 5 or 6 for more than estimated time: BROKEN"); + status6Time = 0; + return status; + } else { + status6Time = time(nullptr); + statusPos = position; + } + } + } + } + + /* + If the axis is moving, set the corresponding flags in the pmacController (pC_) and end the poll. + */ + if (!moving_to_position) { + *moving = true; + st = setIntegerParam(pC_->motorStatusMoving_, true); + status = status > st ? status : st; + st = setIntegerParam(pC_->motorStatusDone_, false); + return status > st ? status : st; + } else { + *moving = false; + st = setIntegerParam(pC_->motorStatusMoving_, false); + status = status > st ? status : st; + st = setIntegerParam(pC_->motorStatusDone_, true); + status = status > st ? status : st; + } + + /* Set any axis specific general problem bits. */ + if (axError != 0) { + axisProblemFlag = 1; + if (axisErrorCount < 10) { + axMessage = translateAxisError(axError); + asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, + "drvGirderAxisGetStatus: Axis %d is in deep trouble: axis error " + "code %d, translated: %s:, status code = %d\n", + axisNo_, axError, axMessage, axStat); + snprintf(message, sizeof(message), "GirderAxis error: %s", axMessage); + updateMsgTxtFromDriver(message); + if (axMessage != NULL) { + free(axMessage); + } + axisErrorCount++; + } else if (axisErrorCount == 10) { + asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, + "Suppressing further axis error messages\n"); + axisErrorCount++; + } + } else { + axisProblemFlag = 0; + axisErrorCount = 0; + } + + st = setIntegerParam(pC_->motorStatusProblem_, axisProblemFlag); + status = status > st ? status : st; + + return status; +} \ No newline at end of file diff --git a/sinqEPICSApp/src/pmacAxis.h b/sinqEPICSApp/src/pmacAxis.h index 658a2ec..1fc6454 100644 --- a/sinqEPICSApp/src/pmacAxis.h +++ b/sinqEPICSApp/src/pmacAxis.h @@ -189,4 +189,21 @@ protected: time_t det_startTime; }; +/*----------------------------------------------------------------------------------------------*/ + + +class GirderAxis: public pmacV3Axis { + public: + GirderAxis(pmacController *pController, int axisNo); + asynStatus move(double position, int relative, double min_velocity, double max_velocity, double acceleration); + asynStatus stop(double acceleration); + asynStatus poll(bool *moving); + + protected: + int IsEnable; + + friend class pmacController; + friend class pmacV3Controller; +}; + #endif /* pmacAxis_H */ diff --git a/sinqEPICSApp/src/pmacController.cpp b/sinqEPICSApp/src/pmacController.cpp index 8548e32..71c7e2f 100644 --- a/sinqEPICSApp/src/pmacController.cpp +++ b/sinqEPICSApp/src/pmacController.cpp @@ -781,6 +781,34 @@ pC->unlock(); return asynSuccess; } +/** +* C wrapper for the GirderAxis constructor. +* See GirderAxis::GirderAxis. +* +*/ +asynStatus pmacCreateGirderAxis( + const char *pmacName, /* specify which controller by port name */ + int axis) /* axis number (start from 1). */ +{ + pmacController *pC; + pmacAxis *pAxis; + + static const char *functionName = "pmacCreateGirderAxis"; + + pC = (pmacController *)findAsynPortDriver(pmacName); + if (!pC) { + printf("%s:%s: Error port %s not found\n", driverName, functionName, + pmacName); + return asynError; + } + + pC->lock(); + pAxis = new GirderAxis(pC, axis); + pAxis = NULL; + pC->unlock(); + return asynSuccess; +} + /*================================ SeleneController ===============================================*/ asynStatus SeleneController::writeFloat64(asynUser *pasynUser, epicsFloat64 value) @@ -1057,6 +1085,18 @@ static void configpmacAmorDetectorAxisCallFunc(const iocshArgBuf *args) pmacCreateAmorDetectorAxis(args[0].sval, args[1].ival, args[2].ival); } +/* GirderCreateAxis */ +static const iocshArg pmacCreateGirderAxisArg0 = {"Controller port name", + iocshArgString}; +static const iocshArg pmacCreateGirderAxisArg1 = {"Axis number", iocshArgInt}; +static const iocshArg *const pmacCreateGirderAxisArgs[] = {&pmacCreateGirderAxisArg0, + &pmacCreateGirderAxisArg1}; +static const iocshFuncDef configpmacGirderAxis = {"pmacGirderCreateAxis", 2, + pmacCreateGirderAxisArgs}; + +static void configpmacGirderAxisCallFunc(const iocshArgBuf *args) { + pmacCreateGirderAxis(args[0].sval, args[1].ival); +} static void pmacControllerRegister(void) diff --git a/sinqEPICSApp/src/pmacController.h b/sinqEPICSApp/src/pmacController.h index 559ef60..b87d846 100644 --- a/sinqEPICSApp/src/pmacController.h +++ b/sinqEPICSApp/src/pmacController.h @@ -140,6 +140,7 @@ class pmacController : public SINQController { friend class SeleneAxis; friend class LiftAxis; friend class pmacV3Axis; + friend class GirderAxis; friend class AmorDetectorAxis; }; @@ -182,6 +183,7 @@ public: friend class pmacV3Axis; friend class pmacAxis; + friend class GirderAxis; protected: pmacV3Axis **pAxes_; /**< Array of pointers to axis objects */