Compare commits
8 Commits
dev
...
strict_hom
| Author | SHA1 | Date | |
|---|---|---|---|
| 86e6ab1fdd | |||
| 492f459678 | |||
| 4cb51814b8 | |||
| 0e6c77b456 | |||
| 92c264d0f6 | |||
| a84992b4cd | |||
| 9fee0f3c92 | |||
| 7b8c4db43e |
@@ -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,
|
||||||
|
|||||||
@@ -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")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
139
src/sinqAxis.cpp
139
src/sinqAxis.cpp
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user