From a84992b4cd00edace674c2b4aae0ba567447a4f7 Mon Sep 17 00:00:00 2001 From: smathis Date: Fri, 6 Mar 2026 11:20:33 +0100 Subject: [PATCH] Improved homing doc, fixed wrong setting of motorStatusHomed and added check to move --- README.md | 4 ++-- db/sinqMotor.db | 2 +- src/sinqAxis.cpp | 47 ++++++++++++++++++++++++++++-------------- src/sinqAxis.h | 45 ++++++++++++++++++++++++++-------------- src/sinqController.cpp | 16 +++++++------- src/sinqController.h | 27 +++++++++++++++++++++--- 6 files changed, 96 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 4722edc..284e440 100644 --- a/README.md +++ b/README.md @@ -280,9 +280,9 @@ bookkeeping tasks before and after calling `doPoll`: message is waiting in the temporary buffer, set `motorStatusProblem` to true, otherwise to false. If an old error message is waiting in the temporary buffer, but `doPoll` returned `asynSuccess`, overwrite the paramLib entry for - `motorMessageText` with the old error message. + `motorErrorMessage` with the old error message. - Run `callParamCallbacks` - - Reset `motorMessageText` AFTER updating the PVs. This makes sure that the + - Reset `motorErrorMessage` AFTER updating the PVs. This makes sure that the error message is shown for at least one poll cycle. - Return the status of `doPoll` - `motorPosition`: Returns the parameter library value of the motor position, diff --git a/db/sinqMotor.db b/db/sinqMotor.db index ce7ccd9..6860018 100755 --- a/db/sinqMotor.db +++ b/db/sinqMotor.db @@ -109,7 +109,7 @@ record(ao,"$(INSTR)$(M):RecResolution") { # This record contains messages from the driver (usually error messages). # The macro ERRORMSGSIZE can be used to set the maximum length of the message. # if not provided, a default value of 200 is used. -# This record is coupled to the parameter library via motorMessageText -> MOTOR_MESSAGE_TEXT. +# This record is coupled to the parameter library via motorErrorMessage -> MOTOR_MESSAGE_TEXT. record(waveform, "$(INSTR)$(M):ErrorMessage") { alias("$(INSTR)$(M)-MsgTxt") # Old PV name, aliased for backwards compatibility field(DTYP, "asynOctetRead") diff --git a/src/sinqAxis.cpp b/src/sinqAxis.cpp index accce7e..a7b0a3d 100644 --- a/src/sinqAxis.cpp +++ b/src/sinqAxis.cpp @@ -72,7 +72,7 @@ sinqAxis::sinqAxis(class sinqController *pC, int axisNo) Initialize the parameter library entry for the motor message text, because it is read during the first poll before it has been written to. */ - status = setStringParam(pC_->motorMessageText(), ""); + status = setStringParam(pC_->motorErrorMessage(), ""); if (status != asynSuccess) { asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR, "Controller \"%s\", axis %d => %s, line %d:\nFATAL ERROR " @@ -143,7 +143,7 @@ sinqAxis::sinqAxis(class sinqController *pC, int axisNo) exit(-1); } - // Set the homing-related flags + // Motor is assumed to not being in a homing run at IOC startup. status = setIntegerParam(pC_->motorStatusHome(), 0); if (status != asynSuccess) { asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR, @@ -154,7 +154,9 @@ sinqAxis::sinqAxis(class sinqController *pC, int axisNo) pC_->stringifyAsynStatus(status)); exit(-1); } - status = setIntegerParam(pC_->motorStatusHomed(), 0); + + // Motor is assumed to not be at the home position at IOC startup. + status = setIntegerParam(pC_->motorStatusAtHome(), 0); if (status != asynSuccess) { asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR, "Controller \"%s\", axis %d => %s, line %d:\nFATAL ERROR " @@ -164,7 +166,9 @@ sinqAxis::sinqAxis(class sinqController *pC, int axisNo) pC_->stringifyAsynStatus(status)); exit(-1); } - status = setIntegerParam(pC_->motorStatusAtHome(), 0); + + // Motor is assumed to not have been homed in the past. + status = setIntegerParam(pC_->motorStatusHomed(), 0); if (status != asynSuccess) { asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR, "Controller \"%s\", axis %d => %s, line %d:\nFATAL ERROR " @@ -232,14 +236,14 @@ asynStatus sinqAxis::forcedPoll(bool *moving) { pSinqA_->lastPollTime = ts; /* - If the "motorMessageText" record currently contains an error message, it + If the "motorErrorMessage" record currently contains an error message, it should be shown for at least one poll period. To assure this, it is read out here from the paramLib into "waitingMessage". If no new error message was added to the parameter library at the end of the poll cycle, the "waitingMessage" is briefly put into the paramLib again, then the PVs are updated and then the message text is cleared again. */ - getAxisParamChecked(this, motorMessageText, + getAxisParamChecked(this, motorErrorMessage, static_cast(waitingMessage)); // Clear the communication @@ -271,16 +275,16 @@ asynStatus sinqAxis::forcedPoll(bool *moving) { If doPoll cleared the error message paramLib entry, but an old message is still waiting, set the old message. */ - getAxisParamChecked(this, motorMessageText, + getAxisParamChecked(this, motorErrorMessage, static_cast(newMessage)); if (newMessage[0] == '\0') { - setAxisParamChecked(this, motorMessageText, + setAxisParamChecked(this, motorErrorMessage, static_cast(waitingMessage)); } setAxisParamChecked(this, motorStatusProblem, true); } else { // No errors are waiting -> Clear everything. - setAxisParamChecked(this, motorMessageText, ""); + setAxisParamChecked(this, motorErrorMessage, ""); setAxisParamChecked(this, motorStatusProblem, false); } @@ -330,7 +334,7 @@ asynStatus sinqAxis::forcedPoll(bool *moving) { Delete the error message AFTER updating the PVs so it is not there anymore during the next poll. */ - setAxisParamChecked(this, motorMessageText, ""); + setAxisParamChecked(this, motorErrorMessage, ""); return poll_status; } @@ -348,9 +352,23 @@ asynStatus sinqAxis::move(double position, int relative, double minVelocity, // Status of parameter library operations asynStatus status = asynSuccess; double motorRecRes = 0.0; + char encType[pC_->MAXBUF_] = {0}; + int motorStatHomed = 0; // ========================================================================= + /* + Check if the motor is allowed to move: If the motor hasn't been homed in the + past and has an incremental encoder, it needs to be homed first! + */ + getAxisParamChecked(this, encoderType, &encType); + getAxisParamChecked(this, motorStatusHomed, &motorStatHomed); + if (strcmp(encType, IncrementalEncoder) == 0 && motorStatHomed == 0) { + setAxisParamChecked(this, motorErrorMessage, + "Motor needs to be homed first."); + return asynError; + } + // Store the target position internally getAxisParamChecked(this, motorRecResolution, &motorRecRes); pSinqA_->targetPosition = position * motorRecRes; @@ -367,7 +385,6 @@ asynStatus sinqAxis::move(double position, int relative, double minVelocity, // Since the move command was successfull, we assume that the motor has // started its movement. - setAxisParamChecked(this, motorStatusHomed, false); setAxisParamChecked(this, motorStatusAtHome, false); // Needed for adaptive polling @@ -410,7 +427,7 @@ asynStatus sinqAxis::home(double minVelocity, double maxVelocity, } else if (status == asynError) { // asynError means that we tried to home an absolute encoder - setAxisParamChecked(this, motorMessageText, + setAxisParamChecked(this, motorErrorMessage, "Can't home a motor with absolute encoder"); status = assertConnected(); @@ -547,7 +564,7 @@ asynStatus sinqAxis::setVeloFields(double velo, double vbas, double vmax) { "vmax=%lf.\n", pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, vbas, vmax); - setAxisParamChecked(this, motorMessageText, + setAxisParamChecked(this, motorErrorMessage, "Lower speed limit must not be smaller than " "upper speed limit. Please call the support."); return asynError; @@ -561,7 +578,7 @@ asynStatus sinqAxis::setVeloFields(double velo, double vbas, double vmax) { velo, vbas, vmax); setAxisParamChecked( - this, motorMessageText, + this, motorErrorMessage, "Speed is not inside limits. Set a new valid speed and try " "to move the motor. Otherwise, please call the support."); return asynError; @@ -704,7 +721,7 @@ asynStatus sinqAxis::checkMovTimeoutWatchdog(bool moving) { } setAxisParamChecked( - this, motorMessageText, + this, motorErrorMessage, "Exceeded expected arrival time. Check if the axis is blocked."); setAxisParamChecked(this, motorStatusProblem, true); } else { diff --git a/src/sinqAxis.h b/src/sinqAxis.h index 33588c1..6b1986c 100644 --- a/src/sinqAxis.h +++ b/src/sinqAxis.h @@ -111,10 +111,15 @@ class HIDDEN sinqAxis : public asynMotorAxis { * @brief Perform some standardized operations before and after the concrete `doMove` implementation. - * Wrapper around `doMove` which calculates the (absolute) target position - and stores it in the member variable `targetPosition_`. This member variable - is e.g. used for the movement watchdog. Afterwards, it calls and returns - `doMove`. + Wrapper around `doMove` which performs the following operations: + - If the motor has an incremental encoder and the paramLib entry for + motorStatusHomed returns 0, the move command is not forwarded if the motor + has an incremental encoder. + - It calculates the (absolute) target position and stores it in the member + variable `targetPosition_`. This member variable is e.g. used for the + movement watchdog. + + Afterwards, it calls and returns `doMove`. * * @param position Forwarded to `doMove`. * @param relative Forwarded to `doMove`. @@ -154,16 +159,24 @@ class HIDDEN sinqAxis : public asynMotorAxis { within the driver. * - `motorStatusHome_`: This flag should be set to `1` while the motor is - actively moving toward its home position and to `0` when the home position - is reached. - * - * - `motorStatusHomed_`: This flag should be set to `0` at the start of a - homing command and to 1 once the home position is reached. - * - * - `motorStatusAtHome_`: This flag is similar to `motorStatusHomed_`, but - in addition it should also be `1` when the motor is at its home position, - but wasn't actively homed in order to get there. - * + actively moving toward its home position and to `0` otherwise. + + * - `motorStatusAtHome_`: This flag should be set to `0` at the start of a + homing command and to 1 once the home position is reached. This is obviously + the case right after a homing run. Corresponds to bit 7 of the MSTA field of + the motor record. + + * - `motorStatusHomed_`: This flag should be set to `1` if it is known that + that the motor has been homed since the last power cycle of the controller. + + There are two ways the motor record can get this information: + 1) If a home run has been performed by the driver itself via + `sinqAxis::home` (this function). + 2) If the controller reports that the motor has been homed in the past + (possibly before the IOC startup). This information needs to be provided by + the controller (e.g. in an "init" function). Likewise, if the driver detects + a power cycle of the controller, the driver must set this field back to `0`. + * This function performs the following operations in the given order: * * - Call `doHome()` and forward the parameters @@ -178,8 +191,8 @@ class HIDDEN sinqAxis : public asynMotorAxis { * - If `doHome()` returned anything else: Forward the status. * * The flags `motorStatusHome_`, `motorStatusHomed_` and - `motorStatusAtHome_` are set to their idle values (0, 1 and 1 respectively) - in the `forcedPoll())` method once the homing procedure is finished. + `motorStatusAtHome_` are set to 0, 1 and 1 respectively in the + `forcedPoll())` method once the homing procedure is finished. * * @param minVelocity Forwarded to `doHome`. * @param maxVelocity Forwarded to `doHome`. diff --git a/src/sinqController.cpp b/src/sinqController.cpp index 03f4324..b288879 100644 --- a/src/sinqController.cpp +++ b/src/sinqController.cpp @@ -76,7 +76,7 @@ struct sinqControllerImpl { These integers are indices to paramLib entries and are populated when the parameters are created. See the documentation in db/sinqMotor.db. */ - int motorMessageText; + int motorErrorMessage; int motorReset; int motorEnable; int motorEnableRBV; @@ -130,7 +130,7 @@ sinqController::sinqController(const char *portName, .timeoutEvents = {}, .maxSubsequentTimeouts = 10, .maxSubsequentTimeoutsExceeded = false, - .motorMessageText = 0, + .motorErrorMessage = 0, .motorReset = 0, .motorEnable = 0, .motorEnableRBV = 0, @@ -180,7 +180,7 @@ sinqController::sinqController(const char *portName, // This text is used to forward status messages to NICOS and in turn to the // user. status = createParam("MOTOR_MESSAGE_TEXT", asynParamOctet, - &pSinqC_->motorMessageText); + &pSinqC_->motorErrorMessage); if (status != asynSuccess) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " @@ -504,7 +504,7 @@ asynStatus sinqController::couldNotParseResponse(const char *command, } setAxisParamChecked( - axis, motorMessageText, + axis, motorErrorMessage, "Could not interpret controller response. Please call the support"); setAxisParamChecked(axis, motorStatusCommsError, true); @@ -526,7 +526,7 @@ asynStatus sinqController::paramLibAccessFailed(asynStatus status, // Log the error message and try to propagate it. If propagating fails, // there is nothing we can do here anyway. - setStringParam(motorMessageText(), + setStringParam(motorErrorMessage(), "Accessing paramLib failed. Please call the support."); } @@ -597,7 +597,7 @@ asynStatus sinqController::checkComTimeoutWatchdog(sinqAxis *axis) { asynStatus status = checkComTimeoutWatchdog(axis->axisNo(), errorMessage, MAXBUF_); if (status == asynError) { - setAxisParamChecked(axis, motorMessageText, errorMessage); + setAxisParamChecked(axis, motorErrorMessage, errorMessage); } return status; } @@ -645,7 +645,7 @@ asynStatus sinqController::checkMaxSubsequentTimeouts(int timeoutNo, asynStatus status = checkMaxSubsequentTimeouts(timeoutNo, axis->axisNo(), motorMessage, MAXBUF_); if (status == asynError) { - setAxisParamChecked(axis, motorMessageText, motorMessage); + setAxisParamChecked(axis, motorErrorMessage, motorMessage); } return status; } @@ -709,7 +709,7 @@ asynStatus sinqController::setThresholdComTimeout(time_t comTimeoutWindow, return asynSuccess; } -int sinqController::motorMessageText() { return pSinqC_->motorMessageText; } +int sinqController::motorErrorMessage() { return pSinqC_->motorErrorMessage; } int sinqController::motorReset() { return pSinqC_->motorReset; } int sinqController::motorEnable() { return pSinqC_->motorEnable; } int sinqController::motorEnableRBV() { return pSinqC_->motorEnableRBV; } diff --git a/src/sinqController.h b/src/sinqController.h index bbb52af..6148a36 100644 --- a/src/sinqController.h +++ b/src/sinqController.h @@ -101,7 +101,7 @@ class HIDDEN sinqController : public asynMotorController { * * If accessing the parameter library failed (return status != asynSuccess), calling this function writes a standardized message to both - the IOC shell and the motorMessageText PV. It then returns the input + the IOC shell and the motorErrorMessage PV. It then returns the input status. * * @param status Status of the failed parameter library access @@ -278,7 +278,7 @@ class HIDDEN sinqController : public asynMotorController { * the readback velocity instead of that written into the VELO / JVEL * fields. * - * @return int + * @return Index of the paramLib entry */ int motorVelocity() { return motorVelocity_; } int motorVelBase() { return motorVelBase_; } @@ -307,10 +307,24 @@ class HIDDEN sinqController : public asynMotorController { int motorStatusDirection() { return motorStatusDirection_; } int motorStatusDone() { return motorStatusDone_; } int motorStatusHighLimit() { return motorStatusHighLimit_; } + /** + * @brief Set to 1 if the motor is currently homing and 0 otherwise. + * + * See docstring of the sinqAxis::home function. + * + * @return Index of the paramLib entry + */ int motorStatusAtHome() { return motorStatusAtHome_; } int motorStatusSlip() { return motorStatusSlip_; } int motorStatusPowerOn() { return motorStatusPowerOn_; } int motorStatusFollowingError() { return motorStatusFollowingError_; } + /** + * @brief Set to 1 if the motor is currently at its home position. + * + * See docstring of the sinqAxis::home function. + * + * @return Index of the paramLib entry + */ int motorStatusHome() { return motorStatusHome_; } int motorStatusHasEncoder() { return motorStatusHasEncoder_; } int motorStatusProblem() { return motorStatusProblem_; } @@ -318,6 +332,13 @@ class HIDDEN sinqController : public asynMotorController { int motorStatusGainSupport() { return motorStatusGainSupport_; } int motorStatusCommsError() { return motorStatusCommsError_; } int motorStatusLowLimit() { return motorStatusLowLimit_; } + /** + * @brief Set to 1 if the motor has been homed in the past and 0 otherwise + * + * See docstring of the sinqAxis::home function. + * + * @return Index of the paramLib entry + */ int motorStatusHomed() { return motorStatusHomed_; } // Parameters for passing additional motor record information to the driver @@ -327,7 +348,7 @@ class HIDDEN sinqController : public asynMotorController { // Accessors for additional PVs defined in sinqController (which are hidden // in pSinqC_) - int motorMessageText(); + int motorErrorMessage(); int motorReset(); int motorEnable(); int motorEnableRBV();