From bed245b010cfa26b02c6a5375afa1a0efbaa4572 Mon Sep 17 00:00:00 2001 From: smathis Date: Mon, 10 Mar 2025 14:28:24 +0100 Subject: [PATCH] Added PVs for error reset and status problem reporting and fixed a bug in msgPrintControl --- README.md | 6 ++++-- db/sinqMotor.db | 22 ++++++++++++++++++++-- src/msgPrintControl.cpp | 32 +++++++++++++++++++++++--------- src/msgPrintControl.h | 18 +++++++++++++++--- src/sinqAxis.cpp | 2 ++ src/sinqAxis.h | 13 +++++++++++++ src/sinqController.cpp | 12 ++++++++++++ src/sinqController.h | 1 + 8 files changed, 90 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 7cadf04..3eda19b 100644 --- a/README.md +++ b/README.md @@ -171,7 +171,10 @@ sinqMotor offers a variety of additional methods for children classes to standar - `setMaxSubsequentTimeouts`: Set the limit for the number of subsequent timeouts before the user is informed. #### sinqAxis.h -- `enable`: This function is called if the "Enable" PV from db/sinqMotor.db is set. This is an empty function which should be overwritten by concrete driver implementations. +- `enable`: This function is called if the `$(INSTR)$(M):Enable` PV from db/sinqMotor.db is set. +This is an empty function which should be overwritten by concrete driver implementations. +- `reset`: This function is called when the `$(INSTR)$(M):Reset` PV from db/sinqMotor.db is set. +This is an empty function which should be overwritten by concrete driver implementations. - `move`: This function sets the absolute target position in the parameter library and then calls `doMove`. - `doMove`: This is an empty function which should be overwritten by concrete driver implementations. - `home`: This function sets the internal status flags for the homing process and then calls doHome. @@ -187,7 +190,6 @@ sinqMotor offers a variety of additional methods for children classes to standar - Reset `motorStatusProblem_`, `motorStatusCommsError_` and `motorMessageText_` if `doPoll` returned `asynSuccess` - Run `callParamCallbacks` - Return the status of `doPoll` -- `doPoll`: This is an empty function which should be overwritten by concrete driver implementations. - `setVeloFields`: Populates the motor record fields VELO (actual velocity), VBAS (minimum allowed velocity) and VMAX (maximum allowed velocity) from the driver. - `setAcclField`: Populates the motor record field ACCL from the driver. - `startMovTimeoutWatchdog`: Starts a watchdog for the movement time. This watchdog compares the actual time spent in a movement operation with an expected time, which is calculated based on the distance of the current and the target position. diff --git a/db/sinqMotor.db b/db/sinqMotor.db index 257297c..11fcdf9 100755 --- a/db/sinqMotor.db +++ b/db/sinqMotor.db @@ -41,11 +41,29 @@ record(motor,"$(INSTR)$(M)") field(RMOD,"3") # Retry mode 3 ("In-Position"): This suppresses any retries from the motor record. } +# This PV reads out the 10th bit of the MSTA field of the motor record, which +# is the "motorStatusProblem_" bit. The 10th bit is adressed by 0x0200. If the +# bit is 0, the .VAL field is 0 as well. If the bit is 1, we need to divide by +# 512 (=2^9) in order to set the .VAL field to 1 (and not to 512). +record(calc, "$(INSTR)$(M):StatusProblem") +{ + field(INPA, "$(INSTR)$(M).MSTA CP") + field(CALC, "(A&0x200)/512") +} + +# Call the reset function of the corresponding sinqAxis +# This record is coupled to the parameter library via motorReset_ -> MOTOR_RESET. +record(longout, "$(INSTR)$(M):Reset") { + field(DTYP, "asynInt32") + field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) MOTOR_RESET") + field(PINI, "NO") +} + # This PV allows force-stopping the motor record from within the driver by setting # the motorForceStop_ value in the parameter library to 1. It should be reset to 0 by the driver afterwards. # The implementation strategy is taken from https://epics.anl.gov/tech-talk/2022/msg00464.php. # This record is coupled to the parameter library via motorForceStop_ -> MOTOR_FORCE_STOP. -record(longin, "$(INSTR)$(M):STOP_RBV") +record(longin, "$(INSTR)$(M):StopRBV") { field(DTYP, "asynInt32") field(INP, "@asyn($(CONTROLLER),$(AXIS)) MOTOR_FORCE_STOP") @@ -53,7 +71,7 @@ record(longin, "$(INSTR)$(M):STOP_RBV") field(FLNK, "$(INSTR)$(M):Stop2Field") } record(longout, "$(INSTR)$(M):Stop2Field") { - field(DOL, "$(INSTR)$(M):STOP_RBV CP") + field(DOL, "$(INSTR)$(M):StopRBV CP") field(OUT, "$(INSTR)$(M).STOP") field(OMSL, "closed_loop") } diff --git a/src/msgPrintControl.cpp b/src/msgPrintControl.cpp index fae5c67..caa7bfd 100644 --- a/src/msgPrintControl.cpp +++ b/src/msgPrintControl.cpp @@ -69,13 +69,16 @@ bool msgPrintControl::shouldBePrinted(msgPrintControlKey &key, bool wantToPrint, */ if (map_.find(key) != map_.end()) { if (map_[key] != 0) { - char formattedKey[100] = {0}; - key.format(formattedKey, sizeof(formattedKey)); - asynPrint(pasynUser, ASYN_TRACE_ERROR, - "Controller \"%s\", axis %d => %s, line %d\nError " - "associated with key \"%s\" has been resolved.\n", - key.controller_.c_str(), key.axisNo_, - key.functionName_, key.line_, formattedKey); + if (pasynUser != nullptr) { + char formattedKey[100] = {0}; + key.format(formattedKey, sizeof(formattedKey)); + asynPrint( + pasynUser, ASYN_TRACE_ERROR, + "Controller \"%s\", axis %d => %s, line %d\nError " + "associated with key \"%s\" has been resolved.\n", + key.controller_.c_str(), key.axisNo_, key.functionName_, + key.line_, formattedKey); + } map_[key] = 0; } } @@ -91,9 +94,20 @@ bool msgPrintControl::shouldBePrinted(char *portName, int axisNo, return shouldBePrinted(key, wantToPrint, pasynUser); } -void msgPrintControl::resetCount(msgPrintControlKey &key) { +void msgPrintControl::resetCount(msgPrintControlKey &key, asynUser *pasynUser) { if (map_.find(key) != map_.end()) { - map_[key] = 0; + if (map_[key] != 0) { + if (pasynUser != nullptr) { + char formattedKey[100] = {0}; + key.format(formattedKey, sizeof(formattedKey)); + asynPrint(pasynUser, ASYN_TRACE_ERROR, + "Controller \"%s\", axis %d => %s, line %d\nError " + "associated with key \"%s\" has been resolved.\n", + key.controller_.c_str(), key.axisNo_, + key.functionName_, key.line_, formattedKey); + } + map_[key] = 0; + } } } diff --git a/src/msgPrintControl.h b/src/msgPrintControl.h index 13211d8..fd80969 100644 --- a/src/msgPrintControl.h +++ b/src/msgPrintControl.h @@ -93,6 +93,9 @@ class msgPrintControl { * identify individual messages * @param wantToPrint If the message associated with key should be * printed, this value should be true, otherwise false. + * @param pasynUser If the problem has been resolved (wantToPrint = + * false), a corresponding status message is printed using the given + * asynUser. If this pointer is a nullptr, no message is printed. * @return bool If true, the message should be printed, if * false, it should not. */ @@ -108,13 +111,22 @@ class msgPrintControl { * @param fileName * @param line * @param wantToPrint - * @return true - * @return false + * @param pasynUser */ bool shouldBePrinted(char *controller, int axisNo, const char *functionName, int line, bool wantToPrint, asynUser *pasynUser); - void resetCount(msgPrintControlKey &key); + /** + * @brief Reset the error message count incremented in shouldBePrinted for + * the given key + * + * @param key Key associated with the message, used to + * identify individual messages + * @param pasynUser If the problem has been resolved (wantToPrint = + * false), a corresponding status message is printed using the given + * asynUser. If this pointer is a nullptr, no message is printed. + */ + void resetCount(msgPrintControlKey &key, asynUser *pasynUser); /** * @brief Maximum number of times a message is printed before it is diff --git a/src/sinqAxis.cpp b/src/sinqAxis.cpp index 403f82d..a1775bb 100644 --- a/src/sinqAxis.cpp +++ b/src/sinqAxis.cpp @@ -309,6 +309,8 @@ asynStatus sinqAxis::doHome(double minVelocity, double maxVelocity, return asynSuccess; } +asynStatus sinqAxis::reset() { return asynSuccess; } + asynStatus sinqAxis::enable(bool on) { return asynSuccess; } asynStatus sinqAxis::setVeloFields(double velo, double vbas, double vmax) { diff --git a/src/sinqAxis.h b/src/sinqAxis.h index 3ef98ef..8c3aad1 100644 --- a/src/sinqAxis.h +++ b/src/sinqAxis.h @@ -160,10 +160,23 @@ class epicsShareClass sinqAxis : public asynMotorAxis { virtual asynStatus doHome(double minVelocity, double maxVelocity, double acceleration, int forwards); + /** + * @brief This function is called when the PV "$(INSTR)$(M):Reset" is set to + * any value. This method should be implemented by a child class of + * sinqAxis. + * + * @return asynStatus + */ + virtual asynStatus reset(); + /** * @brief This function enables / disables an axis. It should be implemented * by a child class of sinqAxis. * + * The concrete implementation should (but doesn't need to) follow the + * convetion that a value of 0 disables the axis and any other value enables + * it. + * * @param on * @return asynStatus */ diff --git a/src/sinqController.cpp b/src/sinqController.cpp index cd4dded..a972763 100644 --- a/src/sinqController.cpp +++ b/src/sinqController.cpp @@ -109,6 +109,16 @@ sinqController::sinqController(const char *portName, exit(-1); } + status = createParam("MOTOR_RESET", asynParamInt32, &motorReset_); + 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("MOTOR_ENABLE_RBV", asynParamInt32, &motorEnableRBV_); if (status != asynSuccess) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, @@ -293,6 +303,8 @@ asynStatus sinqController::writeInt32(asynUser *pasynUser, epicsInt32 value) { // Handle custom PVs if (function == motorEnable_) { return axis->enable(value != 0); + } else if (function == motorReset_) { + return axis->reset(); } else if (function == motorForceStop_) { return axis->stop(0.0); } else { diff --git a/src/sinqController.h b/src/sinqController.h index 87bc9ba..7a6a519 100644 --- a/src/sinqController.h +++ b/src/sinqController.h @@ -245,6 +245,7 @@ class epicsShareClass sinqController : public asynMotorController { #define FIRST_SINQMOTOR_PARAM motorMessageText_ int motorMessageText_; + int motorReset_; int motorEnable_; int motorEnableRBV_; int motorCanDisable_;