Compare commits

...

5 Commits
0.5.0 ... 0.6.2

5 changed files with 90 additions and 44 deletions

View File

@ -75,7 +75,8 @@ dbLoadTemplate("$(TOP)/mcu1.substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$
### Substitution file ### Substitution file
The substitution file is a table containing axis-specific information which is used to create the axis-specific PVs. To work with sinqMotor, "mcu1.substitutions" needs to look like this: The substitution file is a table containing axis-specific information which is used to create the axis-specific PVs.
To work with sinqMotor, "mcu1.substitutions" needs to look like this (the order of columns does not matter):
``` ```
file "$(SINQDBPATH)" file "$(SINQDBPATH)"
{ {
@ -93,13 +94,13 @@ The variable `SINQDBPATH` has been set in "mcu1.cmd" before calling `dbLoadTempl
- `AXIS`: Index of the axis, corresponds to the physical connection of the axis to the MCU. - `AXIS`: Index of the axis, corresponds to the physical connection of the axis to the MCU.
- `M`: The full PV name is created by concatenating the variables INSTR, NAME and M. For example, the PV of the first axis would be "SQ:SINQTEST:mcu1:lin1". - `M`: The full PV name is created by concatenating the variables INSTR, NAME and M. For example, the PV of the first axis would be "SQ:SINQTEST:mcu1:lin1".
- `DESC`: Description of the motor. This field is just for documentation and is not needed for operating a motor.
- `EGU`: Engineering units. For a linear motor, this is mm, for a rotaty motor, this is degree. - `EGU`: Engineering units. For a linear motor, this is mm, for a rotaty motor, this is degree.
- `DIR`: If set to "Neg", the axis direction is inverted. - `DIR`: If set to "Neg", the axis direction is inverted.
- `MRES`: This is a scaling factor determining the resolution of the position readback value. For example, 0.001 means a precision of 1 um. A detailed description can be found in section [Motor record resolution MRES](#motor-record-resolution-mres). - `MRES`: This is a scaling factor determining the resolution of the position readback value. For example, 0.001 means a precision of 1 um. A detailed description can be found in section [Motor record resolution MRES](#motor-record-resolution-mres).
#### Optional parameters #### Optional parameters
The default values for those parameters are given for the individual records in db/sinqMotor.db The default values for those parameters are given for the individual records in db/sinqMotor.db
- `DESC`: Description of the motor. This field is just for documentation and is not needed for operating a motor.
- `MSGTEXTSIZE`: Buffer size for the motor message record in characters - `MSGTEXTSIZE`: Buffer size for the motor message record in characters
- `ENABLEMOVWATCHDOG`: Sets `setWatchdogEnabled` during IOC startup to the given value. - `ENABLEMOVWATCHDOG`: Sets `setWatchdogEnabled` during IOC startup to the given value.
- `LIMITSOFFSET`: If the motor limits are read out from the controller, they can - `LIMITSOFFSET`: If the motor limits are read out from the controller, they can

View File

@ -1,18 +1,18 @@
# The main asyn motor record. Some fields are populated from the substitution # The main asyn motor record. Some fields are populated from the substitution
# files via macros: # files via macros:
# - $(INSTR): Name of the instrument, e.g. "SQ:SINQTEST:" # - INSTR: Name of the instrument, e.g. "SQ:SINQTEST:"
# - $(M): Name of the motor in EPICS, e.g. "lin1" # - M: Name of the motor in EPICS, e.g. "lin1"
# - $(DESC): Short description of the motor # - DESC: Short description of the motor. If not given, this is equal to M
# - $(DIR): This value is usually set to "Pos". If the motor axis direction # - DIR: This value is usually set to "Pos". If the motor axis direction
# should be inverted, this value can be set to "Neg" # should be inverted, this value can be set to "Neg"
# - $(CONTROLLER): Name of the motor controller, e.g. "mcu1" # - CONTROLLER: Name of the motor controller, e.g. "mcu1"
# - $(AXIS): Number of the axis, e.g. "1" # - AXIS: Number of the axis, e.g. "1"
# - $(MRES): Motor record resolution. See the README.md for a detailed discussion # - MRES: Motor record resolution. See the README.md for a detailed discussion
# - $(EGU): Engineering units. In case of a rotary axis, this is "degree", in # - EGU: Engineering units. In case of a rotary axis, this is "degree", in
# case of a linear axis this is "mm". # case of a linear axis this is "mm".
record(motor,"$(INSTR)$(M)") record(motor,"$(INSTR)$(M)")
{ {
field(DESC,"$(DESC)") field(DESC,"$(DESC=$(M))")
field(DTYP,"asynMotor") field(DTYP,"asynMotor")
field(DIR,"$(DIR=Pos)") field(DIR,"$(DIR=Pos)")
field(OUT,"@asyn($(CONTROLLER),$(AXIS))") field(OUT,"@asyn($(CONTROLLER),$(AXIS))")

View File

@ -5,32 +5,63 @@
sinqAxis::sinqAxis(class sinqController *pC, int axisNo) sinqAxis::sinqAxis(class sinqController *pC, int axisNo)
: asynMotorAxis((asynMotorController *)pC, axisNo), pC_(pC) { : asynMotorAxis((asynMotorController *)pC, axisNo), pC_(pC) {
asynStatus status = asynSuccess;
initial_poll_ = true; initial_poll_ = true;
watchdogMovActive_ = false; watchdogMovActive_ = false;
init_poll_counter_ = 0; init_poll_counter_ = 0;
scaleMovTimeout_ = 2.0; scaleMovTimeout_ = 2.0;
offsetMovTimeout_ = 30; offsetMovTimeout_ = 30;
}
asynStatus sinqAxis::atFirstPoll() {
asynStatus status = asynSuccess;
int variableSpeed = 0;
// Motor is assumed to be enabled // Motor is assumed to be enabled
status = pC_->setIntegerParam(axisNo_, pC_->motorEnableRBV_, 1); status = pC_->setIntegerParam(axisNo_, pC_->motorEnableRBV_, 1);
if (status != asynSuccess) { if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "motorEnableRBV_", asynPrint(
__PRETTY_FUNCTION__, __LINE__); pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"%s => line %d:\nFATAL ERROR (setting a parameter value failed "
"with %s)\n. Terminating IOC",
__PRETTY_FUNCTION__, __LINE__, pC_->stringifyAsynStatus(status));
exit(-1);
} }
// By default, motors cannot be disabled // By default, motors cannot be disabled
status = pC_->setIntegerParam(axisNo_, pC_->motorCanDisable_, 0); status = pC_->setIntegerParam(axisNo_, pC_->motorCanDisable_, 0);
if (status != asynSuccess) { if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "motorCanDisable_", asynPrint(
__PRETTY_FUNCTION__, __LINE__); pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"%s => line %d:\nFATAL ERROR (setting a parameter value failed "
"with %s)\n. Terminating IOC",
__PRETTY_FUNCTION__, __LINE__, pC_->stringifyAsynStatus(status));
exit(-1);
} }
// Provide a default value for the motor position.
status = pC_->setDoubleParam(axisNo_, pC_->motorPosition_, 0.0);
if (status != asynSuccess) {
asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"%s => line %d:\nFATAL ERROR (setting a parameter value failed "
"with %s)\n. Terminating IOC",
__PRETTY_FUNCTION__, __LINE__, pC_->stringifyAsynStatus(status));
exit(-1);
}
// We assume that the motor has no status problems initially
status = pC_->setIntegerParam(axisNo_, pC_->motorStatusProblem_, 0);
if (status != asynSuccess) {
asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"%s => line %d:\nFATAL ERROR (setting a parameter value failed "
"with %s)\n. Terminating IOC",
__PRETTY_FUNCTION__, __LINE__, pC_->stringifyAsynStatus(status));
exit(-1);
}
}
asynStatus sinqAxis::atFirstPoll() {
asynStatus status = asynSuccess;
int variableSpeed = 0;
status = status =
pC_->getIntegerParam(axisNo_, pC_->motorCanSetSpeed_, &variableSpeed); pC_->getIntegerParam(axisNo_, pC_->motorCanSetSpeed_, &variableSpeed);
if (status != asynSuccess) { if (status != asynSuccess) {
@ -100,20 +131,13 @@ asynStatus sinqAxis::poll(bool *moving) {
} }
} }
// The poll function is just a wrapper around doPoll and /*
// handles mainly the callParamCallbacks() function. This wrapper is used At the beginning of the poll, it is assumed that the axis has no status
// to make sure callParamCallbacks() is called in case of a premature problems and therefore all error indicators are reset. This does not affect
// return. the PVs until callParamCallbacks has been called!
poll_status = doPoll(moving);
// Check and update the watchdog The motorStatusProblem_ field changes the motor record fields SEVR and STAT.
if (checkMovTimeoutWatchdog(*moving) != asynSuccess) { */
return asynError;
}
// If the poll status is ok, reset the error indicators in the parameter
// library
if (poll_status == asynSuccess) {
pl_status = setIntegerParam(pC_->motorStatusProblem_, false); pl_status = setIntegerParam(pC_->motorStatusProblem_, false);
if (pl_status != asynSuccess) { if (pl_status != asynSuccess) {
pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_", pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
@ -124,12 +148,31 @@ asynStatus sinqAxis::poll(bool *moving) {
pC_->paramLibAccessFailed(pl_status, "motorStatusCommsError_", pC_->paramLibAccessFailed(pl_status, "motorStatusCommsError_",
__PRETTY_FUNCTION__, __LINE__); __PRETTY_FUNCTION__, __LINE__);
} }
pl_status = setStringParam(pC_->motorMessageText_, ""); pl_status = setStringParam(pC_->motorMessageText_, "");
if (pl_status != asynSuccess) { if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
__PRETTY_FUNCTION__, __LINE__); __PRETTY_FUNCTION__, __LINE__);
} }
// The poll function is just a wrapper around doPoll and
// handles mainly the callParamCallbacks() function. This wrapper is used
// to make sure callParamCallbacks() is called in case of a premature
// return.
poll_status = doPoll(moving);
// The poll did not succeed: Something went wrong and the motor has a status
// problem.
if (poll_status != asynSuccess) {
pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
if (pl_status != asynSuccess) {
pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
__PRETTY_FUNCTION__, __LINE__);
}
}
// Check and update the watchdog
if (checkMovTimeoutWatchdog(*moving) != asynSuccess) {
return asynError;
} }
if (pl_status != asynSuccess) { if (pl_status != asynSuccess) {
@ -490,8 +533,9 @@ asynStatus sinqAxis::checkMovTimeoutWatchdog(bool moving) {
// Check the watchdog // Check the watchdog
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"%s => line %d:\nAxis %d exceeded the expected arrival time " "%s => line %d:\nAxis %d exceeded the expected arrival time "
"(%ld).\n", "%ld (current time is %ld).\n",
__PRETTY_FUNCTION__, __LINE__, axisNo_, expectedArrivalTime_); __PRETTY_FUNCTION__, __LINE__, axisNo_, expectedArrivalTime_,
time(NULL));
pl_status = setStringParam( pl_status = setStringParam(
pC_->motorMessageText_, pC_->motorMessageText_,

View File

@ -290,7 +290,7 @@ asynStatus sinqController::errMsgCouldNotParseResponse(const char *command,
asynStatus pl_status = asynSuccess; asynStatus pl_status = asynSuccess;
asynPrint(lowLevelPortUser_, ASYN_TRACE_ERROR, asynPrint(lowLevelPortUser_, ASYN_TRACE_ERROR,
"%s => line %d:\nCould not interpret response %s for " "%s => line %d:\nCould not interpret response '%s' for "
"command %s.\n", "command %s.\n",
functionName, lineNumber, response, command); functionName, lineNumber, response, command);

View File

@ -10,8 +10,9 @@ Stefan Mathis, November 2024
#define motorMessageIsFromDriverString "MOTOR_MESSAGE_DRIVER" #define motorMessageIsFromDriverString "MOTOR_MESSAGE_DRIVER"
#define motorMessageTextString "MOTOR_MESSAGE_TEXT" #define motorMessageTextString "MOTOR_MESSAGE_TEXT"
#define IncrementalEncoder "Incremental encoder" #define IncrementalEncoder "incremental"
#define AbsoluteEncoder "Absolute encoder" #define AbsoluteEncoder "absolute"
#define NoEncoder "none"
class epicsShareClass sinqController : public asynMotorController { class epicsShareClass sinqController : public asynMotorController {
public: public: