Added some flags for NICOS and refactored some records from pmacv3 to

sinqMotor
This commit is contained in:
2024-11-29 14:54:54 +01:00
parent 682325de7d
commit 6656841a01
6 changed files with 394 additions and 63 deletions

View File

@ -58,11 +58,11 @@ pattern
```
The variable `sinqMotor_DB` is generated automatically by `require` and corresponds to the module database path.
The other parameters have the following meaning:
- `AXIS`: Index of the axis, corresponds to the physical connection of the axis to the MCU
- `M`: Name of the motor as shown in EPICS
- `AXIS`: Index of the axis, corresponds to the physical connection of the axis to the MCU.
- `M`: Name of the motor as shown in EPICS.
- `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
- `DIR`: If set to "Neg", the axis direction is inverted
- `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.
- `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 discussion of this value can be found in sinqAxis.cspp/startMovTimeoutWatchdog.
- `MOVWATCHDOG`: Sets `setWatchdogEnabled` during IOC startup to the given value.
- `LIMITSOFFSET`: If the motor limits are read out from the controller, they can be further reduced by this offset in order to avoid errors due to slight overshoot on the motor controller. For example, if this value is 1.0 and the read-out limits are [-10.0 10.0], the EPICS limits are set to [-9.0 9.0].
@ -77,4 +77,4 @@ All existing tags can be listed with `git tag` in the sinqMotor directory. Detai
The makefile in the top directory includes all necessary steps for compiling a shared library together with the header files into `/ioc/modules` (using the PSI EPICS build system).Therefore it is sufficient to run `make install` from the terminal.
To use the library when writing a concrete motor driver, include it in the makefile of your application /library the same way as other libraries such as e.g. `asynMotor` by adding `REQUIRED+=sinqMotor` to your Makefile. The version can be specified with `sinqMotor_VERSION=x.x.x.`
To use the library when writing a concrete motor driver, include it in the makefile of your application / library the same way as other libraries such as e.g. `asynMotor` by adding `REQUIRED+=sinqMotor` to your Makefile. The version can be specified with `sinqMotor_VERSION=x.x.x.`

View File

@ -17,13 +17,13 @@ record(waveform, "$(P)$(M)-MsgTxt") {
field(DTYP, "asynOctetRead")
field(INP, "@asyn($(CONTROLLER),$(AXIS),1) MOTOR_MESSAGE_TEXT")
field(FTVL, "CHAR")
field(NELM, "$(MsgTxtSize=200)") # Should be the same as MAXBUF in the driver code
field(NELM, "$(MSGTEXTSIZE=200)") # Should be the same as MAXBUF in the driver code
field(SCAN, "I/O Intr")
}
# Provides the motor resolution MRES via an additional PV as explained here:
# https://epics.anl.gov/tech-talk/2020/msg00378.php
record(ao,"$(P)$(M):Resolution") {
record(ao,"$(P)$(M):RecResolution") {
field(DESC, "$(M) resolution")
field(DOL, "$(P)$(M).MRES CP MS")
field(OMSL, "closed_loop")
@ -31,24 +31,35 @@ record(ao,"$(P)$(M):Resolution") {
field(OUT, "@asyn($(CONTROLLER),$(AXIS)) MOTOR_REC_RESOLUTION")
}
# Disables the motor for an input of zero and enables it otherwise
record(longout, "$(P)$(M):Enable") {
field(DTYP, "asynInt32")
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) ENABLE_AXIS")
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) MOTOR_ENABLE")
field(PINI, "NO")
}
# If this PV value is zero, the motor is disabled, otherwise it is enabled
record(longin, "$(P)$(M):Enable_RBV") {
field(DTYP, "asynInt32")
field(INP, "@asyn($(CONTROLLER),$(AXIS),1) ENABLE_AXIS_RBV")
field(INP, "@asyn($(CONTROLLER),$(AXIS),1) MOTOR_ENABLE_RBV")
field(PINI, "NO")
field(SCAN, "I/O Intr")
}
record(longout, "$(P)$(M):EnableWatchdog") {
# If this PV value is zero, the motor cannot be disabled, otherwise it can
record(longin, "$(P)$(M):CanDisable") {
field(DTYP, "asynInt32")
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) ENABLE_MOV_WATCHDOG")
field(INP, "@asyn($(CONTROLLER),$(AXIS),1) MOTOR_CAN_DISABLE")
field(PINI, "NO")
field(SCAN, "I/O Intr")
}
record(longout, "$(P)$(M):CanSetSpeed") {
field(DTYP, "asynInt32")
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) MOTOR_CAN_SET_SPEED")
field(PINI, "YES")
field(VAL, "$(MOVWATCHDOG)")
field(ASG, "READONLY") # Field is initialized during IOC startup
field(VAL, "$(CANSETSPEED=0)")
}
# The high and low limits of the axis are read
@ -59,10 +70,10 @@ record(longout, "$(P)$(M):EnableWatchdog") {
# low limit.
record(ao, "$(P)$(M):LimitsOffset") {
field(DTYP, "asynFloat64")
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) LIMITS_OFFSET")
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) MOTOR_LIMITS_OFFSET")
field(PINI, "YES")
field(ASG, "READONLY") # Field is initialized during IOC startup
field(VAL, "$(LIMITSOFFSET)")
field(VAL, "$(LIMITSOFFSET=0)")
}
# ===================================================================
@ -71,33 +82,100 @@ record(ao, "$(P)$(M):LimitsOffset") {
# This strategy is described here: https://epics.anl.gov/tech-talk/2022/msg00464.php
# Helper record for the high limit which is filled in by the driver
record(ai, "$(P)$(M):MOTOR_HIGH_LIMIT-RBV")
record(ai, "$(P)$(M):DHLM_RBV")
{
field(DTYP, "asynFloat64")
field(INP, "@asyn($(CONTROLLER),$(AXIS)) MOTOR_HIGH_LIMIT_FROM_DRIVER")
field(SCAN, "I/O Intr")
field(FLNK, "$(P)$(M):PUSH_DHLM_TO_FIELD")
field(FLNK, "$(P)$(M):PushDHLM2Field")
}
# Push the value into the field of the main motor record
record(ao, "$(P)$(M):PUSH_DHLM_TO_FIELD") {
field(DOL, "$(P)$(M):MOTOR_HIGH_LIMIT-RBV CP")
record(ao, "$(P)$(M):PushDHLM2Field") {
field(DOL, "$(P)$(M):DHLM_RBV CP")
field(OUT, "$(P)$(M).DHLM")
field(OMSL, "closed_loop") # This configuration keeps the input value $(P)$(M):HLM_ADD_OFFSET and the output field $(P)$(M).DHLM in sync
field(OMSL, "closed_loop")
}
# Helper record for the low limit which is filled in by the driver
record(ai, "$(P)$(M):MOTOR_LOW_LIMIT-RBV")
record(ai, "$(P)$(M):DLLM_RBV")
{
field(DTYP, "asynFloat64")
field(INP, "@asyn($(CONTROLLER),$(AXIS)) MOTOR_LOW_LIMIT_FROM_DRIVER")
field(SCAN, "I/O Intr")
field(FLNK, "$(P)$(M):PUSH_DLLM_TO_FIELD")
field(FLNK, "$(P)$(M):PushDLLM2Field")
}
# Push the value into the field of the main motor record
record(ao, "$(P)$(M):PUSH_DLLM_TO_FIELD") {
field(DOL, "$(P)$(M):MOTOR_LOW_LIMIT-RBV CP")
record(ao, "$(P)$(M):PushDLLM2Field") {
field(DOL, "$(P)$(M):DLLM_RBV CP")
field(OUT, "$(P)$(M).DLLM")
field(OMSL, "closed_loop") # This configuration keeps the input value $(P)$(M):LLM_ADD_OFFSET and the output field $(P)$(M).DLLM in sync
field(OMSL, "closed_loop")
}
record(longout, "$(P)$(M):EnableMovWatchdog") {
field(DTYP, "asynInt32")
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) MOTOR_ENABLE_MOV_WATCHDOG")
field(PINI, "YES")
field(VAL, "$(ENABLEMOVWATCHDOG=0)")
}
# ===================================================================
# The following PVs set the velocity values from the driver
# Helper record for the high limit which is filled in by the driver
record(ai, "$(P)$(M):VELO_RBV")
{
field(DTYP, "asynFloat64")
field(INP, "@asyn($(CONTROLLER),$(AXIS)) MOTOR_VELO_FROM_DRIVER")
field(SCAN, "I/O Intr")
field(FLNK, "$(P)$(M):PushVELO2Field")
}
record(ao, "$(P)$(M):PushVELO2Field") {
field(DOL, "$(P)$(M):VELO_RBV CP")
field(OUT, "$(P)$(M).VELO")
field(OMSL, "closed_loop")
}
record(ai, "$(P)$(M):VBAS_RBV")
{
field(DTYP, "asynFloat64")
field(INP, "@asyn($(CONTROLLER),$(AXIS)) MOTOR_VBAS_FROM_DRIVER")
field(SCAN, "I/O Intr")
field(FLNK, "$(P)$(M):PushVBAS2Field")
}
record(ao, "$(P)$(M):PushVBAS2Field") {
field(DOL, "$(P)$(M):VBAS_RBV CP")
field(OUT, "$(P)$(M).VBAS")
field(OMSL, "closed_loop") # This configuration keeps the PV and the field in sync
}
record(ai, "$(P)$(M):VMAX_RBV")
{
field(DTYP, "asynFloat64")
field(INP, "@asyn($(CONTROLLER),$(AXIS)) MOTOR_VMAX_FROM_DRIVER")
field(SCAN, "I/O Intr")
field(FLNK, "$(P)$(M):PushVMAX2Field")
}
record(ao, "$(P)$(M):PushVMAX2Field") {
field(DOL, "$(P)$(M):VMAX_RBV CP")
field(OUT, "$(P)$(M).VMAX")
field(OMSL, "closed_loop") # This configuration keeps the PV and the field in sync
}
record(ai, "$(P)$(M):ACCL_RBV")
{
field(DTYP, "asynFloat64")
field(INP, "@asyn($(CONTROLLER),$(AXIS)) MOTOR_ACCL_FROM_DRIVER")
field(SCAN, "I/O Intr")
field(FLNK, "$(P)$(M):PushACCL2Field")
}
record(ao, "$(P)$(M):PushACCL2Field") {
field(DOL, "$(P)$(M):ACCL_RBV CP")
field(OUT, "$(P)$(M).ACCL")
field(OMSL, "closed_loop") # This configuration keeps the PV and the field in sync
}

View File

@ -13,7 +13,44 @@ sinqAxis::sinqAxis(class sinqController *pC, int axisNo)
offsetMovTimeout_ = 30;
}
asynStatus sinqAxis::atFirstPoll() { return asynSuccess; }
asynStatus sinqAxis::atFirstPoll() {
asynStatus status = asynSuccess;
int variableSpeed = 0;
status =
pC_->getIntegerParam(axisNo_, pC_->motorCanSetSpeed_, &variableSpeed);
if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "motorCanSetSpeed_",
__PRETTY_FUNCTION__, __LINE__);
}
if (variableSpeed == 1) {
status = pC_->setDoubleParam(axisNo_, pC_->motorVbasFromDriver_, 0.0);
if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "motorVbasFromDriver_",
__PRETTY_FUNCTION__, __LINE__);
}
status = pC_->setDoubleParam(axisNo_, pC_->motorVmaxFromDriver_,
1000000000.0);
if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "motorVmaxFromDriver_",
__PRETTY_FUNCTION__, __LINE__);
}
} else {
status = pC_->setDoubleParam(axisNo_, pC_->motorVbasFromDriver_, 0.0);
if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "motorVbasFromDriver_",
__PRETTY_FUNCTION__, __LINE__);
}
status = pC_->setDoubleParam(axisNo_, pC_->motorVmaxFromDriver_, 0.0);
if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "motorVmaxFromDriver_",
__PRETTY_FUNCTION__, __LINE__);
}
}
return status;
}
asynStatus sinqAxis::poll(bool *moving) {
// Local variable declaration
@ -98,9 +135,9 @@ asynStatus sinqAxis::poll(bool *moving) {
__PRETTY_FUNCTION__, __LINE__);
}
} else {
pl_status = setIntegerParam(pC_->enableMotorRBV_, axisIsEnabled);
pl_status = setIntegerParam(pC_->motorEnableRBV_, axisIsEnabled);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "enableMotorRBV_",
return pC_->paramLibAccessFailed(pl_status, "motorEnableRBV_",
__PRETTY_FUNCTION__, __LINE__);
}
}
@ -224,18 +261,117 @@ asynStatus sinqAxis::isEnabled(bool *on) {
return asynSuccess;
}
asynStatus sinqAxis::setVeloFields(double velo, double vbas, double vmax) {
asynStatus status = asynSuccess;
int variableSpeed = 0;
// Can the speed of the motor be varied?
status =
pC_->getIntegerParam(axisNo_, pC_->motorCanSetSpeed_, &variableSpeed);
if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "motorCanSetSpeed_",
__PRETTY_FUNCTION__, __LINE__);
}
if (variableSpeed == 1) {
// Check the inputs and create corresponding error messages
if (vbas > vmax) {
asynPrint(pC_->lowLevelPortUser_, ASYN_TRACE_ERROR,
"%s => line %d:\nLower speed limit vbas=%lf must not be "
"smaller than upper limit vmax=%lf.\n",
__PRETTY_FUNCTION__, __LINE__, vbas, vmax);
status = setStringParam(
pC_->motorMessageText_,
"Lower speed limit must not be smaller than upper speed limit");
if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "motorMessageText_",
__PRETTY_FUNCTION__, __LINE__);
}
return asynError;
}
if (velo < vbas || velo > vmax) {
asynPrint(pC_->lowLevelPortUser_, ASYN_TRACE_ERROR,
"%s => line %d:\nActual speed velo=%lf must be between "
"lower limit vbas=%lf and upper limit vmx=%lf.\n",
__PRETTY_FUNCTION__, __LINE__, velo, vbas, vmax);
status = setStringParam(pC_->motorMessageText_,
"Speed is not inside limits");
if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "motorMessageText_",
__PRETTY_FUNCTION__, __LINE__);
}
return asynError;
}
status = pC_->setDoubleParam(axisNo_, pC_->motorVbasFromDriver_, vbas);
if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "motorVbasFromDriver_",
__PRETTY_FUNCTION__, __LINE__);
}
status = pC_->setDoubleParam(axisNo_, pC_->motorVeloFromDriver_, velo);
if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "motorVeloFromDriver_",
__PRETTY_FUNCTION__, __LINE__);
}
status = pC_->setDoubleParam(axisNo_, pC_->motorVmaxFromDriver_, vmax);
if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "motorVmaxFromDriver_",
__PRETTY_FUNCTION__, __LINE__);
}
} else {
// Set minimum and maximum speed equal to the set speed
status = pC_->setDoubleParam(axisNo_, pC_->motorVbasFromDriver_, velo);
if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "motorVbasFromDriver_",
__PRETTY_FUNCTION__, __LINE__);
}
status = pC_->setDoubleParam(axisNo_, pC_->motorVeloFromDriver_, velo);
if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "motorVeloFromDriver_",
__PRETTY_FUNCTION__, __LINE__);
}
status = pC_->setDoubleParam(axisNo_, pC_->motorVmaxFromDriver_, velo);
if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "motorVmaxFromDriver_",
__PRETTY_FUNCTION__, __LINE__);
}
}
return status;
}
asynStatus sinqAxis::setAcclField(double accl) {
if (accl <= 0.0) {
return asynError;
}
asynStatus status =
pC_->setDoubleParam(axisNo_, pC_->motorAcclFromDriver_, accl);
if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "motorAcclFromDriver_",
__PRETTY_FUNCTION__, __LINE__);
}
return status;
}
asynStatus sinqAxis::setWatchdogEnabled(bool enable) {
return pC_->setIntegerParam(axisNo_, pC_->enableMovWatchdog_, enable);
return pC_->setIntegerParam(axisNo_, pC_->motorEnableMovWatchdog_, enable);
}
asynStatus sinqAxis::startMovTimeoutWatchdog() {
asynStatus pl_status;
int enableMovWatchdog = 0;
pl_status = pC_->getIntegerParam(axisNo_, pC_->enableMovWatchdog_,
pl_status = pC_->getIntegerParam(axisNo_, pC_->motorEnableMovWatchdog_,
&enableMovWatchdog);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "enableMovWatchdog_",
return pC_->paramLibAccessFailed(pl_status, "motorEnableMovWatchdog_",
__PRETTY_FUNCTION__, __LINE__);
}
@ -385,10 +521,10 @@ asynStatus sinqAxis::checkMovTimeoutWatchdog(bool moving) {
asynStatus pl_status;
int enableMovWatchdog = 0;
pl_status = pC_->getIntegerParam(axisNo_, pC_->enableMovWatchdog_,
pl_status = pC_->getIntegerParam(axisNo_, pC_->motorEnableMovWatchdog_,
&enableMovWatchdog);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "enableMovWatchdog_",
return pC_->paramLibAccessFailed(pl_status, "motorEnableMovWatchdog_",
__PRETTY_FUNCTION__, __LINE__);
}

View File

@ -43,7 +43,7 @@ class epicsShareClass sinqAxis : public asynMotorAxis {
- Reset motorStatusProblem_, motorStatusCommsError_ and motorMessageText_ if
doPoll returned asynSuccess
- If the movement timeout watchdog has been started, check it.
- Update the parameter library entry enableMotorRBV_ by calling isEnabled.
- Update the parameter library entry motorEnableRBV_ by calling isEnabled.
- Run `callParamCallbacks`
- Return the status of `doPoll`
*
@ -162,6 +162,44 @@ class epicsShareClass sinqAxis : public asynMotorAxis {
*/
virtual asynStatus isEnabled(bool *on);
/**
* @brief Populate the motor record fields VELO, VBAS and VMAX
*
* Populates the speed fields of the motor record. If the param lib
* entry motorCanSetSpeed_ (connected to the PV x:VariableSpeed) is set to
* 1, VBAS and VMAX are set to min and max respectively. Otherwise, they are
* set to val. Additionally, the speed itself is set to velo.
*
* The units of the inputs are engineering units (EGU) per second (e.g. mm/s
* if the EGU is mm).
*
* If the given configuration is invalid (min > max, velo < min, velo > max)
* and the motor is configured as a variable speed motor (param lib entry
* motorCanSetSpeed_ is 1), this function returns an asynError.
*
* @param velo Actual velocity (EGU / s)
* @param vbas Minimum allowed velocity (EGU / s)
* @param velo Maximum allowed velocity (EGU / s)
*
* @return asynStatus
*/
virtual asynStatus setVeloFields(double velo, double vbas, double vmax);
/**
* @brief Populate the ACCL field of the motor record
*
* Populates the acceleration field of the motor record with the given
* value. If accl is not positive, this function does not set the value and
* returns an asynError.
*
* The unit of the input is engineering units (EGU) per second squared (e.g.
* mm/s^2 if the EGU is mm).
*
* @param accl Actual acceleration (EGU / s^2)
* @return asynStatus
*/
virtual asynStatus setAcclField(double accl);
/**
* @brief Start the watchdog for the movement, if the watchdog is not
disabled. See the documentation of checkMovTimeoutWatchdog for more details.

View File

@ -81,7 +81,7 @@ sinqController::sinqController(const char *portName,
exit(-1);
}
status = createParam("ENABLE_AXIS", asynParamInt32, &enableMotor_);
status = createParam("MOTOR_ENABLE", asynParamInt32, &motorEnable_);
if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s => line %d:\nFATAL ERROR (creating a parameter failed "
@ -90,7 +90,37 @@ sinqController::sinqController(const char *portName,
exit(-1);
}
status = createParam("ENABLE_AXIS_RBV", asynParamInt32, &enableMotorRBV_);
status = createParam("MOTOR_ENABLE_RBV", asynParamInt32, &motorEnableRBV_);
if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s => line %d:\nFATAL ERROR (creating a parameter failed "
"with %s).\nTerminating IOC",
__PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status));
exit(-1);
}
status =
createParam("MOTOR_CAN_DISABLE", asynParamInt32, &motorCanDisable_);
if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s => line %d:\nFATAL ERROR (creating a parameter failed "
"with %s).\nTerminating IOC",
__PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status));
exit(-1);
}
status =
createParam("MOTOR_CAN_SET_SPEED", asynParamInt32, &motorCanSetSpeed_);
if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s => line %d:\nFATAL ERROR (creating a parameter failed "
"with %s).\nTerminating IOC",
__PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status));
exit(-1);
}
status = createParam("MOTOR_LIMITS_OFFSET", asynParamFloat64,
&motorLimitsOffset_);
if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s => line %d:\nFATAL ERROR (creating a parameter failed "
@ -124,8 +154,8 @@ sinqController::sinqController(const char *portName,
exit(-1);
}
status =
createParam("ENABLE_MOV_WATCHDOG", asynParamInt32, &enableMovWatchdog_);
status = createParam("MOTOR_ENABLE_MOV_WATCHDOG", asynParamInt32,
&motorEnableMovWatchdog_);
if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s => line %d:\nFATAL ERROR (creating a parameter failed "
@ -134,8 +164,38 @@ sinqController::sinqController(const char *portName,
exit(-1);
}
status =
createParam("LIMITS_OFFSET", asynParamFloat64, &motorLimitsOffset_);
status = createParam("MOTOR_VELO_FROM_DRIVER", asynParamFloat64,
&motorVeloFromDriver_);
if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s => line %d:\nFATAL ERROR (creating a parameter failed "
"with %s).\nTerminating IOC",
__PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status));
exit(-1);
}
status = createParam("MOTOR_VBAS_FROM_DRIVER", asynParamFloat64,
&motorVbasFromDriver_);
if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s => line %d:\nFATAL ERROR (creating a parameter failed "
"with %s).\nTerminating IOC",
__PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status));
exit(-1);
}
status = createParam("MOTOR_VMAX_FROM_DRIVER", asynParamFloat64,
&motorVmaxFromDriver_);
if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s => line %d:\nFATAL ERROR (creating a parameter failed "
"with %s).\nTerminating IOC",
__PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status));
exit(-1);
}
status = createParam("MOTOR_ACCL_FROM_DRIVER", asynParamFloat64,
&motorAcclFromDriver_);
if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s => line %d:\nFATAL ERROR (creating a parameter failed "
@ -178,20 +238,21 @@ asynStatus sinqController::writeInt32(asynUser *pasynUser, epicsInt32 value) {
}
// Handle custom PVs
if (function == enableMotor_) {
if (function == motorEnable_) {
return axis->enable(value != 0);
} else {
return asynMotorController::writeInt32(pasynUser, value);
}
}
/*
Overloaded from asynMotorController to cover the readback value of enableMotor.
*/
asynStatus sinqController::readInt32(asynUser *pasynUser, epicsInt32 *value) {
if (pasynUser->reason == enableMotorRBV_) {
if (pasynUser->reason == motorEnableRBV_) {
// Value is updated in the poll function of an axis
return asynSuccess;
} else if (pasynUser->reason == motorCanDisable_) {
// By default, motors cannot be disabled
*value = 0;
return asynSuccess;
} else {
return asynMotorController::readInt32(pasynUser, value);
}
@ -199,18 +260,30 @@ asynStatus sinqController::readInt32(asynUser *pasynUser, epicsInt32 *value) {
asynStatus sinqController::errMsgCouldNotParseResponse(const char *command,
const char *response,
int axisNo_,
int axisNo,
const char *functionName,
int lineNumber) {
asynStatus pl_status = asynSuccess;
asynPrint(lowLevelPortUser_, ASYN_TRACE_ERROR,
"%s => line %d:\n Could not interpret response %s for "
"%s => line %d:\nCould not interpret response %s for "
"command %s.\n",
functionName, lineNumber, response, command);
setStringParam(motorMessageText_,
"Could not interpret MCU response. Please call the support");
setIntegerParam(motorStatusCommsError_, 1);
pl_status = setStringParam(
motorMessageText_,
"Could not interpret MCU response. Please call the support");
if (pl_status != asynSuccess) {
return paramLibAccessFailed(pl_status, "motorMessageText_",
__PRETTY_FUNCTION__, __LINE__);
}
pl_status = setIntegerParam(motorStatusCommsError_, 1);
if (pl_status != asynSuccess) {
return paramLibAccessFailed(pl_status, "motorStatusCommsError_",
__PRETTY_FUNCTION__, __LINE__);
}
return asynError;
}
@ -220,7 +293,8 @@ asynStatus sinqController::paramLibAccessFailed(asynStatus status,
int lineNumber) {
if (status != asynSuccess) {
// Log the error message and try to propagate it
// Log the error message and try to propagate it. If propagating fails,
// there is nothing we can do here anyway.
asynPrint(lowLevelPortUser_, ASYN_TRACE_ERROR,
"%s => line %d:\n Accessing the parameter library failed for "
"parameter %s with error %s.\n",

View File

@ -55,7 +55,7 @@ class epicsShareClass sinqController : public asynMotorController {
* @param value New value
* @return asynStatus
*/
asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value);
virtual asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value);
/**
* @brief Overloaded function of asynMotorController
@ -67,7 +67,7 @@ class epicsShareClass sinqController : public asynMotorController {
* @param value Read-out value
* @return asynStatus
*/
asynStatus readInt32(asynUser *pasynUser, epicsInt32 *value);
virtual asynStatus readInt32(asynUser *pasynUser, epicsInt32 *value);
/**
* @brief Error handling in case accessing the parameter library failed.
@ -107,7 +107,7 @@ class epicsShareClass sinqController : public asynMotorController {
* @return asynStatus Returns asynError.
*/
asynStatus errMsgCouldNotParseResponse(const char *command,
const char *response, int axisNo_,
const char *response, int axisNo,
const char *functionName,
int lineNumber);
@ -127,18 +127,23 @@ class epicsShareClass sinqController : public asynMotorController {
#define FIRST_SINQMOTOR_PARAM motorMessageText_
int motorMessageText_;
int motorTargetPosition_;
int enableMotor_;
int enableMotorRBV_;
int enableMovWatchdog_;
int motorEnable_;
int motorEnableRBV_;
int motorCanDisable_;
int motorEnableMovWatchdog_;
int motorCanSetSpeed_;
int motorLimitsOffset_;
/*
These parameters are here to write the high and low limits from the MCU to
the EPICS motor record. Using motorHighLimit_ / motorLowLimit_ does not
work: https://epics.anl.gov/tech-talk/2023/msg00576.php.
Therefore, some additional records are introduced which read from these
parameters and write into the motor record. See the sinq_asyn_motor.db file.
These parameters are here to write values from the hardware to the EPICS
motor record. Using motorHighLimit_ / motorLowLimit_ does not work:
https://epics.anl.gov/tech-talk/2023/msg00576.php. Therefore, some
additional records are introduced which read from these parameters and write
into the motor record.
*/
int motorVeloFromDriver_;
int motorVbasFromDriver_;
int motorVmaxFromDriver_;
int motorAcclFromDriver_;
int motorHighLimitFromDriver_;
int motorLowLimitFromDriver_;
#define LAST_SINQMOTOR_PARAM motorLowLimitFromDriver_