Compare commits

..

6 Commits
1.4.0 ... 1.5.1

Author SHA1 Message Date
3ab40a8bf5 Fixed compiler warnings
Some checks failed
Test And Build / Lint (push) Failing after 2s
Test And Build / Build (push) Successful in 7s
2025-09-17 10:52:56 +02:00
0478854007 Added compiler flags to improve code quality 2025-09-17 10:25:50 +02:00
9a32532c22 Expanded error messageto give users the ability to help themselves
All checks were successful
Test And Build / Lint (push) Successful in 5s
Test And Build / Build (push) Successful in 6s
2025-09-17 10:24:07 +02:00
cff64f5ecf Added new feature to set deadband
All checks were successful
Test And Build / Lint (push) Successful in 6s
Test And Build / Build (push) Successful in 12s
The field SPDB can now be populated via either the substitutions file or
from inside the driver (using the motorPositionDeadband paramLib entry).
2025-09-09 16:50:26 +02:00
7a0de4e9d9 Perform callParamCallbacks even if movement watchdog timed out
All checks were successful
Test And Build / Lint (push) Successful in 6s
Test And Build / Build (push) Successful in 5s
2025-08-14 17:15:21 +02:00
566728c57c Fixed error in README.md
All checks were successful
Test And Build / Lint (push) Successful in 5s
Test And Build / Build (push) Successful in 5s
2025-08-12 09:15:55 +02:00
8 changed files with 114 additions and 41 deletions

View File

@@ -26,6 +26,7 @@ TEMPLATES += db/sinqMotor.db
# This file registers the motor-specific functions in the IOC shell. # This file registers the motor-specific functions in the IOC shell.
DBDS += src/sinqMotor.dbd DBDS += src/sinqMotor.dbd
USR_CFLAGS += -Wall -Wextra -Weffc++ -Wunused-result # -Werror USR_CFLAGS += -Wall -Wextra -Wunused-result # -Werror
USR_CXXFLAGS += -Wall -Wextra -Wunused-result
# MISCS would be the place to keep the stream device template files # MISCS would be the place to keep the stream device template files

View File

@@ -257,7 +257,7 @@ This method should not be called in the driver code itself if a poll is needed -
- `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. - `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.
- `checkMovTimeoutWatchdog`: Check if the watchdog timed out. - `checkMovTimeoutWatchdog`: Check if the watchdog timed out.
- `setWatchdogEnabled`: Enables / disables the watchdog. This function is also available in the IOC shell. - `setWatchdogEnabled`: Enables / disables the watchdog. This function is also available in the IOC shell.
- `setOffsetMovTimeout`: Set a linear offset for the expected movement time. This function is also available in the IOC shell. - `setOffsetMovTimeout`: Set a constant offset for the expected movement time. This function is also available in the IOC shell.
- `setScaleMovTimeout`: Set a scaling factor for the expected movement time. This function is also available in the IOC shell. - `setScaleMovTimeout`: Set a scaling factor for the expected movement time. This function is also available in the IOC shell.
#### msgPrintControl.h #### msgPrintControl.h

View File

@@ -41,6 +41,7 @@ record(motor,"$(INSTR)$(M)")
field(PINI,"NO") field(PINI,"NO")
field(DHLM, "$(DHLM=0)") field(DHLM, "$(DHLM=0)")
field(DLLM, "$(DLLM=0)") field(DLLM, "$(DLLM=0)")
field(SPDB, "$(SPDB=0)")
field(TWV,"1") field(TWV,"1")
field(RTRY,"0") field(RTRY,"0")
field(RDBD, "$(RDBD=10e300)") # Suppress retries and overshoot stop commands field(RDBD, "$(RDBD=10e300)") # Suppress retries and overshoot stop commands
@@ -238,6 +239,27 @@ record(ao, "$(INSTR)$(M):PushDLLM2Field") {
field(PINI, "NO") field(PINI, "NO")
} }
# This record pair reads the parameter library value for "motorPositionDeadband"
# and pushes it to the motor record field "SPDP". This can be used to the position
# deadband from the hardware
# The implementation strategy is taken from https://epics.anl.gov/tech-talk/2022/msg00464.php.
# This record is coupled to the parameter library via motorPositionDeadband -> MOTOR_POSITION_DEADBAND.
record(ai, "$(INSTR)$(M):SPDB_RBV")
{
field(DTYP, "asynFloat64")
field(VAL, "$(SPDP=0)")
field(INP, "@asyn($(CONTROLLER),$(AXIS)) MOTOR_POSITION_DEADBAND")
field(SCAN, "I/O Intr")
field(FLNK, "$(INSTR)$(M):PushSPDB2Field")
field(PINI, "NO")
}
record(ao, "$(INSTR)$(M):PushSPDB2Field") {
field(DOL, "$(INSTR)$(M):SPDB_RBV NPP")
field(OUT, "$(INSTR)$(M).SPDB")
field(OMSL, "closed_loop")
field(PINI, "NO")
}
# This record pair reads the parameter library value for "motorVeloFromDriver_" # This record pair reads the parameter library value for "motorVeloFromDriver_"
# and pushes it to the motor record field "VELO". This can be used to read the speed value # and pushes it to the motor record field "VELO". This can be used to read the speed value
# from the hardware and correspondingly update the motor record from the driver. # from the hardware and correspondingly update the motor record from the driver.

View File

@@ -92,8 +92,8 @@ bool msgPrintControl::shouldBePrinted(char *portName, int axisNo,
const char *functionName, int line, const char *functionName, int line,
bool wantToPrint, asynUser *pasynUser, bool wantToPrint, asynUser *pasynUser,
size_t maxRepetitions) { size_t maxRepetitions) {
msgPrintControlKey key = msgPrintControlKey key = msgPrintControlKey(portName, axisNo, functionName,
msgPrintControlKey(portName, axisNo, functionName, __LINE__); line, maxRepetitions);
return shouldBePrinted(key, wantToPrint, pasynUser); return shouldBePrinted(key, wantToPrint, pasynUser);
} }

View File

@@ -283,9 +283,12 @@ asynStatus sinqAxis::forcedPoll(bool *moving) {
pSinqA_->wasMoving = *moving; pSinqA_->wasMoving = *moving;
} }
// Check and update the watchdog // Check and update the watchdog as well as the general poll status IF the
if (checkMovTimeoutWatchdog(*moving) != asynSuccess) { // poll did not fail already.
return asynError; if (poll_status == asynSuccess) {
if (checkMovTimeoutWatchdog(*moving) != asynSuccess) {
poll_status = asynError;
}
} }
// According to the function documentation of asynMotorAxis::poll, this // According to the function documentation of asynMotorAxis::poll, this
@@ -315,7 +318,7 @@ asynStatus sinqAxis::forcedPoll(bool *moving) {
return poll_status; return poll_status;
} }
asynStatus sinqAxis::doPoll(bool *moving) { return asynSuccess; } asynStatus sinqAxis::doPoll(bool * /*moving*/) { return asynSuccess; }
asynStatus sinqAxis::move(double position, int relative, double minVelocity, asynStatus sinqAxis::move(double position, int relative, double minVelocity,
double maxVelocity, double acceleration) { double maxVelocity, double acceleration) {
@@ -351,8 +354,9 @@ asynStatus sinqAxis::move(double position, int relative, double minVelocity,
return pC_->callParamCallbacks(); return pC_->callParamCallbacks();
} }
asynStatus sinqAxis::doMove(double position, int relative, double minVelocity, asynStatus sinqAxis::doMove(double /*position*/, int /*relative*/,
double maxVelocity, double acceleration) { double /*minVelocity*/, double /*maxVelocity*/,
double /*acceleration*/) {
return asynSuccess; return asynSuccess;
} }
@@ -394,8 +398,8 @@ asynStatus sinqAxis::home(double minVelocity, double maxVelocity,
} }
} }
asynStatus sinqAxis::doHome(double minVelocity, double maxVelocity, asynStatus sinqAxis::doHome(double /*minVelocity*/, double /*maxVelocity*/,
double acceleration, int forwards) { double /*acceleration*/, int /*forwards*/) {
return asynSuccess; return asynSuccess;
} }
@@ -417,7 +421,7 @@ asynStatus sinqAxis::reset() {
asynStatus sinqAxis::doReset() { return asynError; } asynStatus sinqAxis::doReset() { return asynError; }
asynStatus sinqAxis::enable(bool on) { return asynSuccess; } asynStatus sinqAxis::enable(bool /*on*/) { return asynSuccess; }
asynStatus sinqAxis::motorPosition(double *motorPos) { asynStatus sinqAxis::motorPosition(double *motorPos) {
asynStatus status = asynSuccess; asynStatus status = asynSuccess;
@@ -479,9 +483,9 @@ 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( setAxisParamChecked(this, motorMessageText,
this, motorMessageText, "Lower speed limit must not be smaller than "
"Lower speed limit must not be smaller than upper speed limit"); "upper speed limit. Please call the support.");
return asynError; return asynError;
} }
if (velo < vbas || velo > vmax) { if (velo < vbas || velo > vmax) {
@@ -492,8 +496,10 @@ asynStatus sinqAxis::setVeloFields(double velo, double vbas, double vmax) {
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
velo, vbas, vmax); velo, vbas, vmax);
setAxisParamChecked(this, motorMessageText, setAxisParamChecked(
"Speed is not inside limits"); this, motorMessageText,
"Speed is not inside limits. Set a new valid speed and try "
"to move the motor. Otherwise, please call the support.");
return asynError; return asynError;
} }
@@ -706,8 +712,9 @@ static const iocshArg setWatchdogEnabledArg2 = {
"Enabling / disabling the watchdog", iocshArgInt}; "Enabling / disabling the watchdog", iocshArgInt};
static const iocshArg *const setWatchdogEnabledArgs[] = { static const iocshArg *const setWatchdogEnabledArgs[] = {
&setWatchdogEnabledArg0, &setWatchdogEnabledArg1, &setWatchdogEnabledArg2}; &setWatchdogEnabledArg0, &setWatchdogEnabledArg1, &setWatchdogEnabledArg2};
static const iocshFuncDef setWatchdogEnabledDef = {"setWatchdogEnabled", 3, static const iocshFuncDef setWatchdogEnabledDef = {
setWatchdogEnabledArgs}; "setWatchdogEnabled", 3, setWatchdogEnabledArgs,
"Set to 0 to disable the watchdog and to any other value to enable it."};
static void setWatchdogEnabledCallFunc(const iocshArgBuf *args) { static void setWatchdogEnabledCallFunc(const iocshArgBuf *args) {
setWatchdogEnabled(args[0].sval, args[1].ival, args[2].ival); setWatchdogEnabled(args[0].sval, args[1].ival, args[2].ival);
@@ -753,8 +760,9 @@ static const iocshArg setOffsetMovTimeoutArg2 = {"Offset timeout for movement",
static const iocshArg *const setOffsetMovTimeoutArgs[] = { static const iocshArg *const setOffsetMovTimeoutArgs[] = {
&setOffsetMovTimeoutArg0, &setOffsetMovTimeoutArg1, &setOffsetMovTimeoutArg0, &setOffsetMovTimeoutArg1,
&setOffsetMovTimeoutArg2}; &setOffsetMovTimeoutArg2};
static const iocshFuncDef setOffsetMovTimeoutDef = {"setOffsetMovTimeout", 3, static const iocshFuncDef setOffsetMovTimeoutDef = {
setOffsetMovTimeoutArgs}; "setOffsetMovTimeout", 3, setOffsetMovTimeoutArgs,
"Specify an offset (in seconds) for the movement timeout watchdog"};
static void setOffsetMovTimeoutCallFunc(const iocshArgBuf *args) { static void setOffsetMovTimeoutCallFunc(const iocshArgBuf *args) {
setOffsetMovTimeout(args[0].sval, args[1].ival, args[2].dval); setOffsetMovTimeout(args[0].sval, args[1].ival, args[2].dval);
@@ -800,8 +808,9 @@ static const iocshArg setScaleMovTimeoutArg2 = {
"Multiplier for calculated move time", iocshArgDouble}; "Multiplier for calculated move time", iocshArgDouble};
static const iocshArg *const setScaleMovTimeoutArgs[] = { static const iocshArg *const setScaleMovTimeoutArgs[] = {
&setScaleMovTimeoutArg0, &setScaleMovTimeoutArg1, &setScaleMovTimeoutArg2}; &setScaleMovTimeoutArg0, &setScaleMovTimeoutArg1, &setScaleMovTimeoutArg2};
static const iocshFuncDef setScaleMovTimeoutDef = {"setScaleMovTimeout", 3, static const iocshFuncDef setScaleMovTimeoutDef = {
setScaleMovTimeoutArgs}; "setScaleMovTimeout", 3, setScaleMovTimeoutArgs,
"Set a scaling factor for the maximum expected movement time."};
static void setScaleMovTimeoutCallFunc(const iocshArgBuf *args) { static void setScaleMovTimeoutCallFunc(const iocshArgBuf *args) {
setScaleMovTimeout(args[0].sval, args[1].ival, args[2].dval); setScaleMovTimeout(args[0].sval, args[1].ival, args[2].dval);

View File

@@ -606,7 +606,7 @@ template <typename A, typename C>
asynStatus getAxisParamImpl(A *axis, C *controller, const char *indexName, asynStatus getAxisParamImpl(A *axis, C *controller, const char *indexName,
int (C::*func)(), int *readValue, int (C::*func)(), int *readValue,
const char *callerFunctionName, int lineNumber, const char *callerFunctionName, int lineNumber,
size_t msgSize, TypeTag<int>) { size_t /*msgSize*/, TypeTag<int>) {
int indexValue = (controller->*func)(); int indexValue = (controller->*func)();
asynStatus status = asynStatus status =
controller->getIntegerParam(axis->axisNo(), indexValue, readValue); controller->getIntegerParam(axis->axisNo(), indexValue, readValue);
@@ -634,7 +634,7 @@ template <typename A, typename C>
asynStatus getAxisParamImpl(A *axis, C *controller, const char *indexName, asynStatus getAxisParamImpl(A *axis, C *controller, const char *indexName,
int (C::*func)(), double *readValue, int (C::*func)(), double *readValue,
const char *callerFunctionName, int lineNumber, const char *callerFunctionName, int lineNumber,
size_t msgSize, TypeTag<double>) { size_t /*msgSize*/, TypeTag<double>) {
int indexValue = (controller->*func)(); int indexValue = (controller->*func)();
asynStatus status = asynStatus status =
controller->getDoubleParam(axis->axisNo(), indexValue, readValue); controller->getDoubleParam(axis->axisNo(), indexValue, readValue);
@@ -665,7 +665,7 @@ template <typename A, typename C>
asynStatus getAxisParamImpl(A *axis, C *controller, const char *indexName, asynStatus getAxisParamImpl(A *axis, C *controller, const char *indexName,
int (C::*func)(), std::string *readValue, int (C::*func)(), std::string *readValue,
const char *callerFunctionName, int lineNumber, const char *callerFunctionName, int lineNumber,
size_t msgSize, TypeTag<std::string>) { size_t /*msgSize*/, TypeTag<std::string>) {
int indexValue = (controller->*func)(); int indexValue = (controller->*func)();
// Convert the pointer to a reference, since getStringParam expects the // Convert the pointer to a reference, since getStringParam expects the

View File

@@ -92,6 +92,7 @@ struct sinqControllerImpl {
int motorAcclFromDriver; int motorAcclFromDriver;
int motorHighLimitFromDriver; int motorHighLimitFromDriver;
int motorLowLimitFromDriver; int motorLowLimitFromDriver;
int motorPositionDeadband;
int adaptivePolling; int adaptivePolling;
int encoderType; int encoderType;
}; };
@@ -118,15 +119,35 @@ sinqController::sinqController(const char *portName,
asynStatus status = asynSuccess; asynStatus status = asynSuccess;
// The paramLib indices are populated with the calls to createParam // The paramLib indices are populated with the calls to createParam
pSinqC_ = std::make_unique<sinqControllerImpl>( pSinqC_ = std::make_unique<sinqControllerImpl>((sinqControllerImpl){
(sinqControllerImpl){.outstandingForcedFastPolls = 0, .outstandingForcedFastPolls = 0,
.pasynOctetSyncIOipPort = nullptr, .pasynOctetSyncIOipPort = nullptr,
.msgPrintC = msgPrintControl(), .msgPrintC = msgPrintControl(),
.comTimeoutWindow = 3600, .comTimeoutWindow = 3600,
.maxNumberTimeouts = 60, .maxNumberTimeouts = 60,
.timeoutEvents = {}, .timeoutEvents = {},
.maxSubsequentTimeouts = 10, .maxSubsequentTimeouts = 10,
.maxSubsequentTimeoutsExceeded = false}); .maxSubsequentTimeoutsExceeded = false,
.motorMessageText = 0,
.motorReset = 0,
.motorEnable = 0,
.motorEnableRBV = 0,
.motorCanDisable = 0,
.motorEnableMovWatchdog = 0,
.motorCanSetSpeed = 0,
.motorLimitsOffset = 0,
.motorForceStop = 0,
.motorConnected = 0,
.motorVeloFromDriver = 0,
.motorVbasFromDriver = 0,
.motorVmaxFromDriver = 0,
.motorAcclFromDriver = 0,
.motorHighLimitFromDriver = 0,
.motorLowLimitFromDriver = 0,
.motorPositionDeadband = 0,
.adaptivePolling = 0,
.encoderType = 0,
});
// Store the poll period information. The poller itself will be started // Store the poll period information. The poller itself will be started
// later (after the IOC is running in epicsInithookFunction) // later (after the IOC is running in epicsInithookFunction)
@@ -267,6 +288,17 @@ sinqController::sinqController(const char *portName,
exit(-1); exit(-1);
} }
status = createParam("MOTOR_POSITION_DEADBAND", asynParamFloat64,
&pSinqC_->motorPositionDeadband);
if (status != asynSuccess) {
asynPrint(this->pasynUser(), 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_MOV_WATCHDOG", asynParamInt32, status = createParam("MOTOR_ENABLE_MOV_WATCHDOG", asynParamInt32,
&pSinqC_->motorEnableMovWatchdog); &pSinqC_->motorEnableMovWatchdog);
if (status != asynSuccess) { if (status != asynSuccess) {
@@ -699,6 +731,9 @@ int sinqController::motorHighLimitFromDriver() {
int sinqController::motorLowLimitFromDriver() { int sinqController::motorLowLimitFromDriver() {
return pSinqC_->motorLowLimitFromDriver; return pSinqC_->motorLowLimitFromDriver;
} }
int sinqController::motorPositionDeadband() {
return pSinqC_->motorPositionDeadband;
}
int sinqController::adaptivePolling() { return pSinqC_->adaptivePolling; } int sinqController::adaptivePolling() { return pSinqC_->adaptivePolling; }
int sinqController::encoderType() { return pSinqC_->encoderType; } int sinqController::encoderType() { return pSinqC_->encoderType; }
@@ -809,7 +844,8 @@ static const iocshArg *const setThresholdComTimeoutArgs[] = {
&setThresholdComTimeoutArg0, &setThresholdComTimeoutArg1, &setThresholdComTimeoutArg0, &setThresholdComTimeoutArg1,
&setThresholdComTimeoutArg2}; &setThresholdComTimeoutArg2};
static const iocshFuncDef setThresholdComTimeoutDef = { static const iocshFuncDef setThresholdComTimeoutDef = {
"setThresholdComTimeout", 3, setThresholdComTimeoutArgs}; "setThresholdComTimeout", 3, setThresholdComTimeoutArgs,
"Set the communication timeout threshold in seconds"};
static void setThresholdComTimeoutCallFunc(const iocshArgBuf *args) { static void setThresholdComTimeoutCallFunc(const iocshArgBuf *args) {
setThresholdComTimeout(args[0].sval, args[1].ival, args[2].ival); setThresholdComTimeout(args[0].sval, args[1].ival, args[2].ival);
@@ -870,7 +906,9 @@ static const iocshArg SetMaxSubsequentTimeoutsArg1 = {
static const iocshArg *const SetMaxSubsequentTimeoutsArgs[] = { static const iocshArg *const SetMaxSubsequentTimeoutsArgs[] = {
&SetMaxSubsequentTimeoutsArg0, &SetMaxSubsequentTimeoutsArg1}; &SetMaxSubsequentTimeoutsArg0, &SetMaxSubsequentTimeoutsArg1};
static const iocshFuncDef setMaxSubsequentTimeoutsDef = { static const iocshFuncDef setMaxSubsequentTimeoutsDef = {
"setMaxSubsequentTimeouts", 2, SetMaxSubsequentTimeoutsArgs}; "setMaxSubsequentTimeouts", 2, SetMaxSubsequentTimeoutsArgs,
"Set the maximum number of subsequent timeouts before the user receives an "
"error message"};
static void setMaxSubsequentTimeoutsCallFunc(const iocshArgBuf *args) { static void setMaxSubsequentTimeoutsCallFunc(const iocshArgBuf *args) {
setMaxSubsequentTimeouts(args[0].sval, args[1].ival); setMaxSubsequentTimeouts(args[0].sval, args[1].ival);
} }
@@ -924,13 +962,15 @@ asynStatus setForcedFastPolls(const char *portName, int forcedFastPolls) {
static const iocshArg SetForcedFastPollsArg0 = {"Controller name (e.g. mcu1)", static const iocshArg SetForcedFastPollsArg0 = {"Controller name (e.g. mcu1)",
iocshArgString}; iocshArgString};
static const iocshArg SetForcedFastPollsArg1 = { static const iocshArg SetForcedFastPollsArg1 = {
"Number of fast polls after \"waking\" the poller (e.g. after issueing a " "Number of fast polls after \"waking\" the poller (e.g. after issuing a "
"move command).", "move command).",
iocshArgInt}; iocshArgInt};
static const iocshArg *const SetForcedFastPollsArgs[] = { static const iocshArg *const SetForcedFastPollsArgs[] = {
&SetForcedFastPollsArg0, &SetForcedFastPollsArg1}; &SetForcedFastPollsArg0, &SetForcedFastPollsArg1};
static const iocshFuncDef setForcedFastPollsDef = {"setForcedFastPolls", 2, static const iocshFuncDef setForcedFastPollsDef = {
SetForcedFastPollsArgs}; "setForcedFastPolls", 2, SetForcedFastPollsArgs,
"Set the number of fast polls after \"waking\" the poller (e.g. after "
"issuing a move command)."};
static void setForcedFastPollsCallFunc(const iocshArgBuf *args) { static void setForcedFastPollsCallFunc(const iocshArgBuf *args) {
setForcedFastPolls(args[0].sval, args[1].ival); setForcedFastPolls(args[0].sval, args[1].ival);
} }

View File

@@ -315,6 +315,7 @@ class HIDDEN sinqController : public asynMotorController {
int motorAcclFromDriver(); int motorAcclFromDriver();
int motorHighLimitFromDriver(); int motorHighLimitFromDriver();
int motorLowLimitFromDriver(); int motorLowLimitFromDriver();
int motorPositionDeadband();
int adaptivePolling(); int adaptivePolling();
int encoderType(); int encoderType();