Compare commits

..

8 Commits

Author SHA1 Message Date
86e6ab1fdd Improved homing doc, fixed wrong setting of motorStatusHomed and added check to move
All checks were successful
Test And Build / Lint (push) Successful in 6s
Test And Build / Build (push) Successful in 6s
2026-03-10 13:17:45 +01:00
492f459678 Adjusted error message
All checks were successful
Test And Build / Lint (push) Successful in 6s
Test And Build / Build (push) Successful in 6s
2026-03-09 13:03:05 +01:00
4cb51814b8 Added callback 2026-03-09 11:55:33 +01:00
0e6c77b456 Return success if motor needs to be homed first 2026-03-09 11:36:51 +01:00
92c264d0f6 Added error message 2026-03-06 14:25:46 +01:00
a84992b4cd Improved homing doc, fixed wrong setting of motorStatusHomed and added check to move
All checks were successful
Test And Build / Lint (push) Successful in 6s
Test And Build / Build (push) Successful in 6s
2026-03-06 11:20:33 +01:00
9fee0f3c92 Renamed error message PV (kept old name as alias for backwards compatibility) 2026-03-06 08:16:01 +01:00
7b8c4db43e Added small docstring to motorVelocity explaining an important caveat.
All checks were successful
Test And Build / Lint (push) Successful in 5s
Test And Build / Build (push) Successful in 5s
2026-02-13 09:25:27 +01:00
6 changed files with 219 additions and 62 deletions

View File

@@ -117,7 +117,7 @@ does not matter):
file "$(SINQDBPATH)" file "$(SINQDBPATH)"
{ {
pattern pattern
{ AXIS, M, DESC, EGU, DIR, MRES, MSGTEXTSIZE, ENABLEMOVWATCHDOG, LIMITSOFFSET, CANSETSPEED, ADAPTPOLL } { AXIS, M, DESC, EGU, DIR, MRES, ERRORMSGSIZE, ENABLEMOVWATCHDOG, LIMITSOFFSET, CANSETSPEED, ADAPTPOLL }
{ 1, "lin1", "Linear motor doing whatever", mm, Pos, 0.001, 200, 1, 1.0, 1, 1 } { 1, "lin1", "Linear motor doing whatever", mm, Pos, 0.001, 200, 1, 1.0, 1, 1 }
{ 2, "rot1", "First rotary motor", degree, Neg, 0.001, 200, 0, 1.0, 0, 1 } { 2, "rot1", "First rotary motor", degree, Neg, 0.001, 200, 0, 1.0, 0, 1 }
{ 3, "rot2", "Second rotary motor", degree, Pos, 0.001, 200, 0, 0.0, 1, 0 } { 3, "rot2", "Second rotary motor", degree, Pos, 0.001, 200, 0, 0.0, 1, 0 }
@@ -146,7 +146,7 @@ The default values for those parameters are given for the individual records in
`db/sinqMotor.db` `db/sinqMotor.db`
- `DESC`: Description of the motor. This field is just for documentation and is - `DESC`: Description of the motor. This field is just for documentation and is
not needed for operating a motor. Defaults to the motor name. not needed for operating a motor. Defaults to the motor name.
- `MSGTEXTSIZE`: Buffer size for the motor message record in characters. - `ERRORMSGSIZE`: Buffer size for the motor message record in characters.
Defaults to 200 characters Defaults to 200 characters
- `ENABLEMOVWATCHDOG`: Sets `setWatchdogEnabled` during IOC startup to the given - `ENABLEMOVWATCHDOG`: Sets `setWatchdogEnabled` during IOC startup to the given
value. Defaults to 0. value. Defaults to 0.
@@ -280,9 +280,9 @@ bookkeeping tasks before and after calling `doPoll`:
message is waiting in the temporary buffer, set `motorStatusProblem` to true, message is waiting in the temporary buffer, set `motorStatusProblem` to true,
otherwise to false. If an old error message is waiting in the temporary otherwise to false. If an old error message is waiting in the temporary
buffer, but `doPoll` returned `asynSuccess`, overwrite the paramLib entry for buffer, but `doPoll` returned `asynSuccess`, overwrite the paramLib entry for
`motorMessageText` with the old error message. `motorErrorMessage` with the old error message.
- Run `callParamCallbacks` - 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. error message is shown for at least one poll cycle.
- Return the status of `doPoll` - Return the status of `doPoll`
- `motorPosition`: Returns the parameter library value of the motor position, - `motorPosition`: Returns the parameter library value of the motor position,

View File

@@ -107,14 +107,15 @@ record(ao,"$(INSTR)$(M):RecResolution") {
} }
# This record contains messages from the driver (usually error messages). # This record contains messages from the driver (usually error messages).
# The macro MSGTEXTSIZE can be used to set the maximum length of the message. # The macro ERRORMSGSIZE can be used to set the maximum length of the message.
# if not provided, a default value of 200 is used. # 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_ERROR_MESSAGE.
record(waveform, "$(INSTR)$(M)-MsgTxt") { record(waveform, "$(INSTR)$(M):ErrorMessage") {
alias("$(INSTR)$(M)-MsgTxt") # Old PV name, aliased for backwards compatibility
field(DTYP, "asynOctetRead") field(DTYP, "asynOctetRead")
field(INP, "@asyn($(CONTROLLER),$(AXIS),1) MOTOR_MESSAGE_TEXT") field(INP, "@asyn($(CONTROLLER),$(AXIS),1) MOTOR_ERROR_MESSAGE")
field(FTVL, "CHAR") field(FTVL, "CHAR")
field(NELM, "$(MSGTEXTSIZE=200)") # Should be the same as MAXBUF in the driver code field(NELM, "$(ERRORMSGSIZE=200)") # Should be the same as MAXBUF in the driver code
field(SCAN, "I/O Intr") field(SCAN, "I/O Intr")
} }

View File

@@ -35,6 +35,11 @@ struct sinqAxisImpl {
Store the time since the last poll Store the time since the last poll
*/ */
epicsTimeStamp lastPollTime; epicsTimeStamp lastPollTime;
// For this time in seconds, any error message will be kept alive in the
// parameter library.
time_t errorDisplayDuration;
epicsTimeStamp errorInitialAppearance;
}; };
sinqAxis::sinqAxis(class sinqController *pC, int axisNo) sinqAxis::sinqAxis(class sinqController *pC, int axisNo)
@@ -49,6 +54,8 @@ sinqAxis::sinqAxis(class sinqController *pC, int axisNo)
.targetPosition = 0.0, .targetPosition = 0.0,
.wasMoving = false, .wasMoving = false,
.lastPollTime = lastPollTime, .lastPollTime = lastPollTime,
.errorDisplayDuration = 2,
.errorInitialAppearance = lastPollTime,
}); });
}()) { }()) {
@@ -72,7 +79,7 @@ sinqAxis::sinqAxis(class sinqController *pC, int axisNo)
Initialize the parameter library entry for the motor message text, because Initialize the parameter library entry for the motor message text, because
it is read during the first poll before it has been written to. it is read during the first poll before it has been written to.
*/ */
status = setStringParam(pC_->motorMessageText(), ""); status = setStringParam(pC_->motorErrorMessage(), "");
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR, asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nFATAL ERROR " "Controller \"%s\", axis %d => %s, line %d:\nFATAL ERROR "
@@ -143,7 +150,7 @@ sinqAxis::sinqAxis(class sinqController *pC, int axisNo)
exit(-1); 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); status = setIntegerParam(pC_->motorStatusHome(), 0);
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR, asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
@@ -154,7 +161,9 @@ sinqAxis::sinqAxis(class sinqController *pC, int axisNo)
pC_->stringifyAsynStatus(status)); pC_->stringifyAsynStatus(status));
exit(-1); 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) { if (status != asynSuccess) {
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR, asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nFATAL ERROR " "Controller \"%s\", axis %d => %s, line %d:\nFATAL ERROR "
@@ -164,7 +173,9 @@ sinqAxis::sinqAxis(class sinqController *pC, int axisNo)
pC_->stringifyAsynStatus(status)); pC_->stringifyAsynStatus(status));
exit(-1); 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) { if (status != asynSuccess) {
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR, asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nFATAL ERROR " "Controller \"%s\", axis %d => %s, line %d:\nFATAL ERROR "
@@ -232,14 +243,14 @@ asynStatus sinqAxis::forcedPoll(bool *moving) {
pSinqA_->lastPollTime = ts; 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 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 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 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 "waitingMessage" is briefly put into the paramLib again, then the PVs are
updated and then the message text is cleared again. updated and then the message text is cleared again.
*/ */
getAxisParamChecked(this, motorMessageText, getAxisParamChecked(this, motorErrorMessage,
static_cast<char *>(waitingMessage)); static_cast<char *>(waitingMessage));
// Clear the communication // Clear the communication
@@ -271,16 +282,24 @@ asynStatus sinqAxis::forcedPoll(bool *moving) {
If doPoll cleared the error message paramLib entry, but an old message If doPoll cleared the error message paramLib entry, but an old message
is still waiting, set the old message. is still waiting, set the old message.
*/ */
getAxisParamChecked(this, motorMessageText, getAxisParamChecked(this, motorErrorMessage,
static_cast<char *>(newMessage)); static_cast<char *>(newMessage));
if (newMessage[0] == '\0') { if (newMessage[0] == '\0') {
setAxisParamChecked(this, motorMessageText, setAxisParamChecked(this, motorErrorMessage,
static_cast<char *>(waitingMessage)); static_cast<char *>(waitingMessage));
} }
bool statProblem = false;
getAxisParamChecked(this, motorStatusProblem, &statProblem);
if (!statProblem) {
// The error initially appeared at this point in time
pSinqA_->errorInitialAppearance = ts;
}
setAxisParamChecked(this, motorStatusProblem, true); setAxisParamChecked(this, motorStatusProblem, true);
} else { } else {
// No errors are waiting -> Clear everything. // No errors are waiting -> Clear everything.
setAxisParamChecked(this, motorMessageText, ""); setAxisParamChecked(this, motorErrorMessage, "");
setAxisParamChecked(this, motorStatusProblem, false); setAxisParamChecked(this, motorStatusProblem, false);
} }
@@ -326,11 +345,12 @@ asynStatus sinqAxis::forcedPoll(bool *moving) {
poll_status = pl_status; poll_status = pl_status;
} }
/* // Delete the error message after the display duration has been exceeded.
Delete the error message AFTER updating the PVs so it is not there anymore epicsTimeStamp errorRemovalTime = pSinqA_->errorInitialAppearance;
during the next poll. epicsTimeAddSeconds(&errorRemovalTime, pSinqA_->errorDisplayDuration);
*/ if (epicsTimeLessThanEqual(&ts, &errorRemovalTime) == 0) {
setAxisParamChecked(this, motorMessageText, ""); setAxisParamChecked(this, motorErrorMessage, "");
}
return poll_status; return poll_status;
} }
@@ -348,9 +368,31 @@ asynStatus sinqAxis::move(double position, int relative, double minVelocity,
// Status of parameter library operations // Status of parameter library operations
asynStatus status = asynSuccess; asynStatus status = asynSuccess;
double motorRecRes = 0.0; 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) {
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line "
"%d:\nAxis needs to be homed first.\n",
pC_->portName, axisNo(), __PRETTY_FUNCTION__, __LINE__);
setAxisParamChecked(this, motorErrorMessage,
"Motor needs to be homed / referenced first.");
setAxisParamChecked(this, motorStatusProblem, true);
epicsTimeStamp ts;
epicsTimeGetCurrent(&ts);
pSinqA_->errorInitialAppearance = ts;
return pC_->callParamCallbacks();
}
// Store the target position internally // Store the target position internally
getAxisParamChecked(this, motorRecResolution, &motorRecRes); getAxisParamChecked(this, motorRecResolution, &motorRecRes);
pSinqA_->targetPosition = position * motorRecRes; pSinqA_->targetPosition = position * motorRecRes;
@@ -367,7 +409,6 @@ asynStatus sinqAxis::move(double position, int relative, double minVelocity,
// Since the move command was successfull, we assume that the motor has // Since the move command was successfull, we assume that the motor has
// started its movement. // started its movement.
setAxisParamChecked(this, motorStatusHomed, false);
setAxisParamChecked(this, motorStatusAtHome, false); setAxisParamChecked(this, motorStatusAtHome, false);
// Needed for adaptive polling // Needed for adaptive polling
@@ -410,7 +451,7 @@ asynStatus sinqAxis::home(double minVelocity, double maxVelocity,
} else if (status == asynError) { } else if (status == asynError) {
// asynError means that we tried to home an absolute encoder // asynError means that we tried to home an absolute encoder
setAxisParamChecked(this, motorMessageText, setAxisParamChecked(this, motorErrorMessage,
"Can't home a motor with absolute encoder"); "Can't home a motor with absolute encoder");
status = assertConnected(); status = assertConnected();
@@ -547,7 +588,7 @@ asynStatus sinqAxis::setVeloFields(double velo, double vbas, double vmax) {
"vmax=%lf.\n", "vmax=%lf.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
vbas, vmax); vbas, vmax);
setAxisParamChecked(this, motorMessageText, setAxisParamChecked(this, motorErrorMessage,
"Lower speed limit must not be smaller than " "Lower speed limit must not be smaller than "
"upper speed limit. Please call the support."); "upper speed limit. Please call the support.");
return asynError; return asynError;
@@ -561,7 +602,7 @@ asynStatus sinqAxis::setVeloFields(double velo, double vbas, double vmax) {
velo, vbas, vmax); velo, vbas, vmax);
setAxisParamChecked( setAxisParamChecked(
this, motorMessageText, this, motorErrorMessage,
"Speed is not inside limits. Set a new valid speed and try " "Speed is not inside limits. Set a new valid speed and try "
"to move the motor. Otherwise, please call the support."); "to move the motor. Otherwise, please call the support.");
return asynError; return asynError;
@@ -704,7 +745,7 @@ asynStatus sinqAxis::checkMovTimeoutWatchdog(bool moving) {
} }
setAxisParamChecked( setAxisParamChecked(
this, motorMessageText, this, motorErrorMessage,
"Exceeded expected arrival time. Check if the axis is blocked."); "Exceeded expected arrival time. Check if the axis is blocked.");
setAxisParamChecked(this, motorStatusProblem, true); setAxisParamChecked(this, motorStatusProblem, true);
} else { } else {
@@ -725,6 +766,11 @@ asynStatus sinqAxis::setScaleMovTimeout(time_t scaleMovTimeout) {
return asynSuccess; return asynSuccess;
} }
asynStatus sinqAxis::setErrorDisplayDuration(time_t errorDisplayDuration) {
pSinqA_->errorDisplayDuration = errorDisplayDuration;
return asynSuccess;
}
bool sinqAxis::wasMoving() { return pSinqA_->wasMoving; } bool sinqAxis::wasMoving() { return pSinqA_->wasMoving; }
void sinqAxis::setWasMoving(bool wasMoving) { pSinqA_->wasMoving = wasMoving; } void sinqAxis::setWasMoving(bool wasMoving) { pSinqA_->wasMoving = wasMoving; }
@@ -882,12 +928,63 @@ static void setScaleMovTimeoutCallFunc(const iocshArgBuf *args) {
// ============================================================================= // =============================================================================
// This function is made known to EPICS in sinqMotor.dbd and is called by EPICS /**
// in order to register all functions in the IOC shell * @brief Set the (minimum) error display duration (FFI implementation)
*
* @param portName Name of the controller
* @param axisNo Axis number
* @param errorDisplayDuration Minimum display duration
* @return asynStatus
*/
asynStatus setErrorDisplayDuration(const char *portName, int axisNo,
double errorDisplayDuration) {
sinqController *pC;
pC = (sinqController *)findAsynPortDriver(portName);
if (pC == nullptr) {
errlogPrintf("Controller \"%s\" => %s, line %d:\nPort %s not found.",
portName, __PRETTY_FUNCTION__, __LINE__, portName);
return asynError;
}
asynMotorAxis *asynAxis = pC->getAxis(axisNo);
sinqAxis *axis = dynamic_cast<sinqAxis *>(asynAxis);
if (axis == nullptr) {
errlogPrintf("Controller \"%s\" => %s, line %d:\nAxis %d does not "
"exist or is not an instance of sinqAxis.",
portName, __PRETTY_FUNCTION__, __LINE__, axisNo);
}
return axis->setErrorDisplayDuration(errorDisplayDuration);
}
static const iocshArg setErrorDisplayDurationArg0 = {"Controller port name",
iocshArgString};
static const iocshArg setErrorDisplayDurationArg1 = {"Axis number",
iocshArgInt};
static const iocshArg setErrorDisplayDurationArg2 = {
"(Minimum) error display duration", iocshArgDouble};
static const iocshArg *const setErrorDisplayDurationArgs[] = {
&setErrorDisplayDurationArg0, &setErrorDisplayDurationArg1,
&setErrorDisplayDurationArg2};
static const iocshFuncDef setErrorDisplayDurationDef = {
"setErrorDisplayDuration", 3, setErrorDisplayDurationArgs,
"Specify an offset (in seconds) for the movement timeout watchdog"};
static void settErrorDisplayDurationCallFunc(const iocshArgBuf *args) {
setErrorDisplayDuration(args[0].sval, args[1].ival, args[2].dval);
}
// =============================================================================
// This function is made known to EPICS in sinqMotor.dbd and is called by
// EPICS in order to register all functions in the IOC shell
static void sinqAxisRegister(void) { static void sinqAxisRegister(void) {
iocshRegister(&setOffsetMovTimeoutDef, setOffsetMovTimeoutCallFunc); iocshRegister(&setOffsetMovTimeoutDef, setOffsetMovTimeoutCallFunc);
iocshRegister(&setScaleMovTimeoutDef, setScaleMovTimeoutCallFunc); iocshRegister(&setScaleMovTimeoutDef, setScaleMovTimeoutCallFunc);
iocshRegister(&setWatchdogEnabledDef, setWatchdogEnabledCallFunc); iocshRegister(&setWatchdogEnabledDef, setWatchdogEnabledCallFunc);
iocshRegister(&setErrorDisplayDurationDef,
settErrorDisplayDurationCallFunc);
} }
epicsExportRegistrar(sinqAxisRegister); epicsExportRegistrar(sinqAxisRegister);

View File

@@ -111,10 +111,15 @@ class HIDDEN sinqAxis : public asynMotorAxis {
* @brief Perform some standardized operations before and after the concrete * @brief Perform some standardized operations before and after the concrete
`doMove` implementation. `doMove` implementation.
* Wrapper around `doMove` which calculates the (absolute) target position Wrapper around `doMove` which performs the following operations:
and stores it in the member variable `targetPosition_`. This member variable - If the motor has an incremental encoder and the paramLib entry for
is e.g. used for the movement watchdog. Afterwards, it calls and returns motorStatusHomed returns 0, the move command is not forwarded if the motor
`doMove`. 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 position Forwarded to `doMove`.
* @param relative Forwarded to `doMove`. * @param relative Forwarded to `doMove`.
@@ -154,16 +159,24 @@ class HIDDEN sinqAxis : public asynMotorAxis {
within the driver. within the driver.
* - `motorStatusHome_`: This flag should be set to `1` while the motor is * - `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 actively moving toward its home position and to `0` otherwise.
is reached.
* * - `motorStatusAtHome_`: This flag should be set to `0` at the start of a
* - `motorStatusHomed_`: 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
homing command and to 1 once the home position is reached. the case right after a homing run. Corresponds to bit 7 of the MSTA field of
* the motor record.
* - `motorStatusAtHome_`: This flag is similar to `motorStatusHomed_`, but
in addition it should also be `1` when the motor is at its home position, * - `motorStatusHomed_`: This flag should be set to `1` if it is known that
but wasn't actively homed in order to get there. 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: * This function performs the following operations in the given order:
* *
* - Call `doHome()` and forward the parameters * - Call `doHome()` and forward the parameters
@@ -178,8 +191,8 @@ class HIDDEN sinqAxis : public asynMotorAxis {
* - If `doHome()` returned anything else: Forward the status. * - If `doHome()` returned anything else: Forward the status.
* *
* The flags `motorStatusHome_`, `motorStatusHomed_` and * The flags `motorStatusHome_`, `motorStatusHomed_` and
`motorStatusAtHome_` are set to their idle values (0, 1 and 1 respectively) `motorStatusAtHome_` are set to 0, 1 and 1 respectively in the
in the `forcedPoll())` method once the homing procedure is finished. `forcedPoll())` method once the homing procedure is finished.
* *
* @param minVelocity Forwarded to `doHome`. * @param minVelocity Forwarded to `doHome`.
* @param maxVelocity Forwarded to `doHome`. * @param maxVelocity Forwarded to `doHome`.
@@ -324,7 +337,7 @@ class HIDDEN sinqAxis : public asynMotorAxis {
/** /**
* @brief Enable / disable the watchdog. Also available in the IOC shell * @brief Enable / disable the watchdog. Also available in the IOC shell
* (see "extern C" section in sinqController.cpp). * (see "extern C" section in sinqAxis.cpp).
* *
* If enable is set to false and the watchdog is currently running, this * If enable is set to false and the watchdog is currently running, this
* function stops it immediately. * function stops it immediately.
@@ -336,7 +349,7 @@ class HIDDEN sinqAxis : public asynMotorAxis {
/** /**
* @brief Set the offsetMovTimeout. Also available in the IOC shell * @brief Set the offsetMovTimeout. Also available in the IOC shell
* (see "extern C" section in sinqController.cpp). * (see "extern C" section in sinqAxis.cpp).
* *
* See documentation of `checkMovTimeoutWatchdog` for details. * See documentation of `checkMovTimeoutWatchdog` for details.
* *
@@ -347,7 +360,7 @@ class HIDDEN sinqAxis : public asynMotorAxis {
/** /**
* @brief Set the scaleMovTimeout. Also available in the IOC shell * @brief Set the scaleMovTimeout. Also available in the IOC shell
* (see "extern C" section in sinqController.cpp). * (see "extern C" section in sinqAxis.cpp).
* *
See documentation of `checkMovTimeoutWatchdog` for details. See documentation of `checkMovTimeoutWatchdog` for details.
* *
@@ -356,6 +369,18 @@ class HIDDEN sinqAxis : public asynMotorAxis {
*/ */
virtual asynStatus setScaleMovTimeout(time_t scaleMovTimeout); virtual asynStatus setScaleMovTimeout(time_t scaleMovTimeout);
/**
* @brief Set the errorDisplayDuration. Also available in the IOC shell
* (see "extern C" section in sinqAxis.cpp).
*
This time in seconds define for which time an error will be displayed after
its root cause has been resolved.
*
* @param errorDisplayDuration Error display time (in seconds)
* @return asynStatus
*/
virtual asynStatus setErrorDisplayDuration(time_t errorDisplayDuration);
/** /**
* @brief Return the axis number of this axis * @brief Return the axis number of this axis
* *

View File

@@ -76,7 +76,7 @@ struct sinqControllerImpl {
These integers are indices to paramLib entries and are populated when the These integers are indices to paramLib entries and are populated when the
parameters are created. See the documentation in db/sinqMotor.db. parameters are created. See the documentation in db/sinqMotor.db.
*/ */
int motorMessageText; int motorErrorMessage;
int motorReset; int motorReset;
int motorEnable; int motorEnable;
int motorEnableRBV; int motorEnableRBV;
@@ -130,7 +130,7 @@ sinqController::sinqController(const char *portName,
.timeoutEvents = {}, .timeoutEvents = {},
.maxSubsequentTimeouts = 10, .maxSubsequentTimeouts = 10,
.maxSubsequentTimeoutsExceeded = false, .maxSubsequentTimeoutsExceeded = false,
.motorMessageText = 0, .motorErrorMessage = 0,
.motorReset = 0, .motorReset = 0,
.motorEnable = 0, .motorEnable = 0,
.motorEnableRBV = 0, .motorEnableRBV = 0,
@@ -176,11 +176,11 @@ sinqController::sinqController(const char *portName,
// =========================================================================; // =========================================================================;
// MOTOR_MESSAGE_TEXT corresponds to the PV definition inside sinqMotor.db. // MOTOR_ERROR_MESSAGE corresponds to the PV definition inside sinqMotor.db.
// This text is used to forward status messages to NICOS and in turn to the // This text is used to forward status messages to NICOS and in turn to the
// user. // user.
status = createParam("MOTOR_MESSAGE_TEXT", asynParamOctet, status = createParam("MOTOR_ERROR_MESSAGE", asynParamOctet,
&pSinqC_->motorMessageText); &pSinqC_->motorErrorMessage);
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
@@ -504,7 +504,7 @@ asynStatus sinqController::couldNotParseResponse(const char *command,
} }
setAxisParamChecked( setAxisParamChecked(
axis, motorMessageText, axis, motorErrorMessage,
"Could not interpret controller response. Please call the support"); "Could not interpret controller response. Please call the support");
setAxisParamChecked(axis, motorStatusCommsError, true); 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, // Log the error message and try to propagate it. If propagating fails,
// there is nothing we can do here anyway. // there is nothing we can do here anyway.
setStringParam(motorMessageText(), setStringParam(motorErrorMessage(),
"Accessing paramLib failed. Please call the support."); "Accessing paramLib failed. Please call the support.");
} }
@@ -597,7 +597,7 @@ asynStatus sinqController::checkComTimeoutWatchdog(sinqAxis *axis) {
asynStatus status = asynStatus status =
checkComTimeoutWatchdog(axis->axisNo(), errorMessage, MAXBUF_); checkComTimeoutWatchdog(axis->axisNo(), errorMessage, MAXBUF_);
if (status == asynError) { if (status == asynError) {
setAxisParamChecked(axis, motorMessageText, errorMessage); setAxisParamChecked(axis, motorErrorMessage, errorMessage);
} }
return status; return status;
} }
@@ -645,7 +645,7 @@ asynStatus sinqController::checkMaxSubsequentTimeouts(int timeoutNo,
asynStatus status = checkMaxSubsequentTimeouts(timeoutNo, axis->axisNo(), asynStatus status = checkMaxSubsequentTimeouts(timeoutNo, axis->axisNo(),
motorMessage, MAXBUF_); motorMessage, MAXBUF_);
if (status == asynError) { if (status == asynError) {
setAxisParamChecked(axis, motorMessageText, motorMessage); setAxisParamChecked(axis, motorErrorMessage, motorMessage);
} }
return status; return status;
} }
@@ -709,7 +709,7 @@ asynStatus sinqController::setThresholdComTimeout(time_t comTimeoutWindow,
return asynSuccess; return asynSuccess;
} }
int sinqController::motorMessageText() { return pSinqC_->motorMessageText; } int sinqController::motorErrorMessage() { return pSinqC_->motorErrorMessage; }
int sinqController::motorReset() { return pSinqC_->motorReset; } int sinqController::motorReset() { return pSinqC_->motorReset; }
int sinqController::motorEnable() { return pSinqC_->motorEnable; } int sinqController::motorEnable() { return pSinqC_->motorEnable; }
int sinqController::motorEnableRBV() { return pSinqC_->motorEnableRBV; } int sinqController::motorEnableRBV() { return pSinqC_->motorEnableRBV; }

View File

@@ -24,7 +24,7 @@ Stefan Mathis, November 2024
#include <memory> #include <memory>
#define motorMessageIsFromDriverString "MOTOR_MESSAGE_DRIVER" #define motorMessageIsFromDriverString "MOTOR_MESSAGE_DRIVER"
#define motorMessageTextString "MOTOR_MESSAGE_TEXT" #define motorMessageTextString "MOTOR_ERROR_MESSAGE"
#define IncrementalEncoder "incremental" #define IncrementalEncoder "incremental"
#define AbsoluteEncoder "absolute" #define AbsoluteEncoder "absolute"
#define NoEncoder "none" #define NoEncoder "none"
@@ -101,7 +101,7 @@ class HIDDEN sinqController : public asynMotorController {
* *
* If accessing the parameter library failed (return status != * If accessing the parameter library failed (return status !=
asynSuccess), calling this function writes a standardized message to both 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. status.
* *
* @param status Status of the failed parameter library access * @param status Status of the failed parameter library access
@@ -267,6 +267,19 @@ class HIDDEN sinqController : public asynMotorController {
int motorMoveVel() { return motorMoveVel_; } int motorMoveVel() { return motorMoveVel_; }
int motorHome() { return motorHome_; } int motorHome() { return motorHome_; }
int motorStop() { return motorStop_; } int motorStop() { return motorStop_; }
/**
* @brief Index for the paramLib entry which writes the motor speed in steps
* / s into the RVEL field of the motor record.
*
* Important: Only write to this field if the motor is moving. As discussed
* here: https://github.com/epics-modules/motor/pull/236, otherwise a race
* condition might occur and the move or moveVelocity might be called with
* the readback velocity instead of that written into the VELO / JVEL
* fields.
*
* @return Index of the paramLib entry
*/
int motorVelocity() { return motorVelocity_; } int motorVelocity() { return motorVelocity_; }
int motorVelBase() { return motorVelBase_; } int motorVelBase() { return motorVelBase_; }
int motorAccel() { return motorAccel_; } int motorAccel() { return motorAccel_; }
@@ -294,10 +307,24 @@ class HIDDEN sinqController : public asynMotorController {
int motorStatusDirection() { return motorStatusDirection_; } int motorStatusDirection() { return motorStatusDirection_; }
int motorStatusDone() { return motorStatusDone_; } int motorStatusDone() { return motorStatusDone_; }
int motorStatusHighLimit() { return motorStatusHighLimit_; } 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 motorStatusAtHome() { return motorStatusAtHome_; }
int motorStatusSlip() { return motorStatusSlip_; } int motorStatusSlip() { return motorStatusSlip_; }
int motorStatusPowerOn() { return motorStatusPowerOn_; } int motorStatusPowerOn() { return motorStatusPowerOn_; }
int motorStatusFollowingError() { return motorStatusFollowingError_; } 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 motorStatusHome() { return motorStatusHome_; }
int motorStatusHasEncoder() { return motorStatusHasEncoder_; } int motorStatusHasEncoder() { return motorStatusHasEncoder_; }
int motorStatusProblem() { return motorStatusProblem_; } int motorStatusProblem() { return motorStatusProblem_; }
@@ -305,6 +332,13 @@ class HIDDEN sinqController : public asynMotorController {
int motorStatusGainSupport() { return motorStatusGainSupport_; } int motorStatusGainSupport() { return motorStatusGainSupport_; }
int motorStatusCommsError() { return motorStatusCommsError_; } int motorStatusCommsError() { return motorStatusCommsError_; }
int motorStatusLowLimit() { return motorStatusLowLimit_; } 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_; } int motorStatusHomed() { return motorStatusHomed_; }
// Parameters for passing additional motor record information to the driver // Parameters for passing additional motor record information to the driver
@@ -314,7 +348,7 @@ class HIDDEN sinqController : public asynMotorController {
// Accessors for additional PVs defined in sinqController (which are hidden // Accessors for additional PVs defined in sinqController (which are hidden
// in pSinqC_) // in pSinqC_)
int motorMessageText(); int motorErrorMessage();
int motorReset(); int motorReset();
int motorEnable(); int motorEnable();
int motorEnableRBV(); int motorEnableRBV();