Prototype for version 0.3

This commit is contained in:
2024-12-04 13:38:13 +01:00
parent 6656841a01
commit 86006e408a
5 changed files with 196 additions and 147 deletions

View File

@ -9,14 +9,15 @@ This library offers base classes for EPICS motor drivers (`sinqAxis` and `sinqCo
sinqMotor offers a variety of additional methods for children classes to standardize certain patterns (e.g. writing messages to the IOC shell and the motor message PV). For a detailed description, please see the respective function documentation in the .h-files. All of these functions can be overwritten manually if e.g. a completely different implementation of `poll` is required. Some functions are marked as virtual, because they are called from other functions of sinqMotor and therefore need runtime polymorphism. Functions without that marker are not called anywhere in sinqMotor.
### sinqController
- `stringifyAsynStatus`: Convert the enum `asynStatus` into a human-readable string.
- `errMsgCouldNotParseResponse`: Write a standardized message if parsing a device response failed.
- `paramLibAccessFailed`: Write a standardized message if accessing the parameter library failed.
- `stringifyAsynStatus`: Convert the enum `asynStatus` into a human-readable string.
### sinqAxis
- `atFirstPoll`: This function is executed once before the first poll. If it returns anything but `asynSuccess`, it retries.
- `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.
- `setWatchdogEnabled`: Enables / disables the watchdog. This function is also available in the IOC shell.
- `checkMovTimeoutWatchdog`: Manages a watchdog mechanism for movement operations. 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.
- `setOffsetMovTimeout`: Set a linear 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.
- `enable`: This function is called if the "Enable" PV from db/sinqMotor.db is set. This is an empty function which should be overwritten by concrete driver implementations.
@ -37,6 +38,8 @@ sinqMotor offers a variety of additional methods for children classes to standar
- Run `callParamCallbacks`
- Return the status of `doPoll`
- `doPoll`: This is an empty function which should be overwritten by concrete driver implementations.
- `setVeloFields`: Populates the motor record fields VELO (actual velocity), VBAS (minimum allowed velocity) and VMAX (maximum allowed velocity) from the driver.
- `setAcclField`: Populates the motor record field ACCL from the driver.
## Database
@ -51,21 +54,71 @@ with `motor.substitutions` looking like this:
file "$(sinqMotor_DB)/sinqMotor.db"
{
pattern
{ AXIS, M, DESC, EGU, DIR, MRES, MOVWATCHDOG, LIMITSOFFSET }
{ 1, "lin1", "lin1", mm, Pos, 0.001, 1, , 1.0 }
{ 2, "rot1", "rot1", degree, Neg, 0.001, 0, , 1.0 }
{ AXIS, M, DESC, EGU, DIR, MRES, MSGTEXTSIZE, ENABLEMOVWATCHDOG, LIMITSOFFSET, CANSETSPEED }
{ 1, "lin1", "lin1", mm, Pos, 0.001, 200, 1, 1.0, 1 }
{ 2, "rot1", "rot1", degree, Neg, 0.001, 200, 0, 1.0, 0 }
}
```
The variable `sinqMotor_DB` is generated automatically by `require` and corresponds to the module database path.
The other parameters have the following meaning:
### Mandatory parameters
- `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.
- `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].
- `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
The default values for those parameters are given for the individual records in db/sinqMotor.db
- `MSGTEXTSIZE`: Buffer size for the motor message record in characters
- `ENABLEMOVWATCHDOG`: 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]. This parameter uses engineering units (EGU).
- `CANSETSPEED`: If set to 1, the motor speed can be modified by the user.
## Motor record resolution MRES
The motor record resolution (index motorRecResolution_ in the parameter
library, MRES in the motor record) is NOT a conversion factor between
user units (e.g. mm) and motor units (e.g. encoder steps), but a scaling
factor defining the resolution of the position readback field RRBV. This
is due to an implementation detail of EPICS described here:
https://epics.anl.gov/tech-talk/2018/msg00089.php
https://github.com/epics-modules/motor/issues/8
Basically, the position value in the parameter library is a double which
is then truncated to an integer in devMotorAsyn.c (because it was
originally meant for converting from engineering units to encoder steps,
which are by definition integer values). Therefore, if we want a
precision of 1 millimeter, we need to set MRES to 1. If we want one of
1 micrometer, we need to set MRES to 0.001. The readback value needs to
be multiplied with MRES to get the actual value.
In the driver, we use user units. Therefore, when we interact with the
parameter library, we need to account for MRES. This means:
- When writing position or speed to the parameter library, we divide the
value by the motor record resolution.
- When reading position or speed from the parameter library, we multiply
the value with the motor record resolution.
Index and motor record field are coupled as follows:
The parameter motorRecResolution_ is coupled to the field MRES of the
motor record in the following manner:
- In sinqMotor.db, the PV (motor_record_pv_name) MOTOR_REC_RESOLUTION
is defined as a copy of the field (motor_record_pv_name).MRES .
- The PV name MOTOR_REC_RESOLUTION is coupled in asynMotorController.h
to the constant motorRecResolutionString
- ... which in turn is assigned to motorRecResolution_ in
asynMotorController.cpp This way of making the field visible to the
driver is described here:
https://epics.anl.gov/tech-talk/2020/msg00378.php This is a one-way
coupling, changes to the parameter library via setDoubleParam are NOT
transferred to (motor_record_pv_name).MRES or to
(motor_record_pv_name):Resolution.
## Versioning

View File

@ -1,8 +1,20 @@
record(motor,"$(P)$(M)")
# The main asyn motor record. Some fields are populated from the substitution
# files via macros:
# - $(INSTR): Name of the instrument, e.g. "SQ:SINQTEST:"
# - $(M): Name of the motor in EPICS, e.g. "lin1"
# - $(DESC): Short description of the motor
# - $(DIR): This value is usually set to "Pos". If the motor axis direction
# should be inverted, this value can be set to "Neg"
# - $(CONTROLLER): Name of the motor controller, e.g. "mcu1"
# - $(AXIS): Number of the axis, e.g. "1"
# - $(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
# case of a linear axis this is "mm".
record(motor,"$(INSTR)$(M)")
{
field(DESC,"$(DESC)")
field(DTYP,"asynMotor")
field(DIR,"$(DIR)")
field(DIR,"$(DIR=Pos)")
field(OUT,"@asyn($(CONTROLLER),$(AXIS))")
field(MRES,"$(MRES)")
field(EGU,"$(EGU)")
@ -12,8 +24,23 @@ record(motor,"$(P)$(M)")
field(RTRY, "0")
}
# The message text
record(waveform, "$(P)$(M)-MsgTxt") {
# This record forwards the motor record resolution MRES to the parameter library
# entry "MOTOR_REC_RESOLUTION" (solution from https://epics.anl.gov/tech-talk/2020/msg00378.php)
# The value of MRES is needed inside the driver for various calculations (e.g.
# for calculating the estimated time of arrival inside the watchdog).
record(ao,"$(INSTR)$(M):RecResolution") {
field(DESC, "$(M) resolution")
field(DOL, "$(INSTR)$(M).MRES CP MS")
field(OMSL, "closed_loop")
field(DTYP, "asynFloat64")
field(OUT, "@asyn($(CONTROLLER),$(AXIS)) MOTOR_REC_RESOLUTION")
}
# This record contains messages from the driver (usually error messages).
# The macro $(MSGTEXTSIZE) can be used to set the maximum length of the message.
# if not provided, a default value of 200 is used.
# This record is coupled to the parameter library via motorMessageText_ -> MOTOR_MESSAGE_TEXT.
record(waveform, "$(INSTR)$(M)-MsgTxt") {
field(DTYP, "asynOctetRead")
field(INP, "@asyn($(CONTROLLER),$(AXIS),1) MOTOR_MESSAGE_TEXT")
field(FTVL, "CHAR")
@ -21,40 +48,41 @@ record(waveform, "$(P)$(M)-MsgTxt") {
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):RecResolution") {
field(DESC, "$(M) resolution")
field(DOL, "$(P)$(M).MRES CP MS")
field(OMSL, "closed_loop")
field(DTYP, "asynFloat64")
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") {
# User-writable switch which disables the motor for an input of zero and enables
# it otherwise. Some motors can't be disabled in certain states (e.g. during
# movement). This behaviour has to be implemented inside the driver.
# This record is coupled to the parameter library via motorEnable_ -> MOTOR_ENABLE.
record(longout, "$(INSTR)$(M):Enable") {
field(DTYP, "asynInt32")
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") {
# Readback value which returns 1 if the motor is disabled and 0 otherwise.
# This record is coupled to the parameter library via motorEnableRBV_ -> MOTOR_ENABLE_RBV.
record(longin, "$(INSTR)$(M):Enable_RBV") {
field(DTYP, "asynInt32")
field(INP, "@asyn($(CONTROLLER),$(AXIS),1) MOTOR_ENABLE_RBV")
field(PINI, "NO")
field(SCAN, "I/O Intr")
}
# If this PV value is zero, the motor cannot be disabled, otherwise it can
record(longin, "$(P)$(M):CanDisable") {
# Some (older) motors cannot be disabled. This property has to be specified in
# the driver by setting the corresponding parameter library entry motorCanDisable_
# to 0 (its default value is 1).
# This record is coupled to the parameter library via motorCanDisable_ -> MOTOR_CAN_DISABLE.
record(longin, "$(INSTR)$(M):CanDisable") {
field(DTYP, "asynInt32")
field(INP, "@asyn($(CONTROLLER),$(AXIS),1) MOTOR_CAN_DISABLE")
field(PINI, "NO")
field(SCAN, "I/O Intr")
}
record(longout, "$(P)$(M):CanSetSpeed") {
# For some motors, the user might be allowed to adjust the speed within the
# limits specified in the motor record as VBAS and VMAX. This functionality can
# be enabled by setting CANSETSPEED to 1. It is disabled by default.
# This record is coupled to the parameter library via motorCanSetSpeed_ -> MOTOR_CAN_SET_SPEED.
record(longout, "$(INSTR)$(M):CanSetSpeed") {
field(DTYP, "asynInt32")
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) MOTOR_CAN_SET_SPEED")
field(PINI, "YES")
@ -62,13 +90,24 @@ record(longout, "$(P)$(M):CanSetSpeed") {
field(VAL, "$(CANSETSPEED=0)")
}
# The high and low limits of the axis are read
# out directly from the MCU. However, since the axis might slightly
# "overshoot" when moving to a position next to the limits, the MCU might go
# into the "limits hit" error state. To prevent this, this value allows adding
# a small offset, which is subtracted from the high limit and added to the
# The timeout mechanism for movements can be enabled / disabled by setting
# this PV to 1 / 0.
# This record is coupled to the parameter library via motorEnableMovWatchdog -> MOTOR_ENABLE_MOV_WATCHDOG.
record(longout, "$(INSTR)$(M):EnableMovWatchdog") {
field(DTYP, "asynInt32")
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) MOTOR_ENABLE_MOV_WATCHDOG")
field(PINI, "YES")
field(VAL, "$(ENABLEMOVWATCHDOG=0)")
}
# For modern controllers, the high and low limits of the axis are read out
# directly from the hardware. However, since the axis might slightly
# "overshoot" when moving to a position next to the limits, the hardware might
# go into a "limits hit" error state. To prevent this, this value allows adding
# a small offset in EGU, which is subtracted from the high limit and added to the
# low limit.
record(ao, "$(P)$(M):LimitsOffset") {
# This record is coupled to the parameter library via motorLimitsOffset_ -> MOTOR_LIMITS_OFFSET.
record(ao, "$(INSTR)$(M):LimitsOffset") {
field(DTYP, "asynFloat64")
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) MOTOR_LIMITS_OFFSET")
field(PINI, "YES")
@ -76,106 +115,111 @@ record(ao, "$(P)$(M):LimitsOffset") {
field(VAL, "$(LIMITSOFFSET=0)")
}
# ===================================================================
# The following records read the high / low limits from the parameter
# library and copy those values into the corresponding fields of the main motor record.
# 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):DHLM_RBV")
# This record pair reads the parameter library value for "motorHighLimitFromDriver_"
# and pushes it to the motor record field "DHLM". This can be used to read limits
# from the hardware and correspondingly update the motor record from the driver.
# The implementation strategy is taken from https://epics.anl.gov/tech-talk/2022/msg00464.php.
# This record is coupled to the parameter library via motorHighLimitFromDriver_ -> MOTOR_HIGH_LIMIT_FROM_DRIVER.
record(ai, "$(INSTR)$(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):PushDHLM2Field")
field(FLNK, "$(INSTR)$(M):PushDHLM2Field")
}
record(ao, "$(P)$(M):PushDHLM2Field") {
field(DOL, "$(P)$(M):DHLM_RBV CP")
field(OUT, "$(P)$(M).DHLM")
record(ao, "$(INSTR)$(M):PushDHLM2Field") {
field(DOL, "$(INSTR)$(M):DHLM_RBV CP")
field(OUT, "$(INSTR)$(M).DHLM")
field(OMSL, "closed_loop")
}
# Helper record for the low limit which is filled in by the driver
record(ai, "$(P)$(M):DLLM_RBV")
# This record pair reads the parameter library value for "motorLowLimitFromDriver_"
# and pushes it to the motor record field "DLLM". This can be used to read limits
# from the hardware and correspondingly update the motor record from the driver.
# The implementation strategy is taken from https://epics.anl.gov/tech-talk/2022/msg00464.php.
# This record is coupled to the parameter library via motorLowLimitFromDriver_ -> MOTOR_LOW_LIMIT_FROM_DRIVER.
record(ai, "$(INSTR)$(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):PushDLLM2Field")
field(FLNK, "$(INSTR)$(M):PushDLLM2Field")
}
# Push the value into the field of the main motor record
record(ao, "$(P)$(M):PushDLLM2Field") {
field(DOL, "$(P)$(M):DLLM_RBV CP")
field(OUT, "$(P)$(M).DLLM")
record(ao, "$(INSTR)$(M):PushDLLM2Field") {
field(DOL, "$(INSTR)$(M):DLLM_RBV CP")
field(OUT, "$(INSTR)$(M).DLLM")
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")
# 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
# from the hardware and correspondingly update the motor record from the driver.
# The implementation strategy is taken from https://epics.anl.gov/tech-talk/2022/msg00464.php.
# This record is coupled to the parameter library via motorVeloFromDriver_ -> MOTOR_VELO_FROM_DRIVER.
record(ai, "$(INSTR)$(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")
field(FLNK, "$(INSTR)$(M):PushVELO2Field")
}
record(ao, "$(P)$(M):PushVELO2Field") {
field(DOL, "$(P)$(M):VELO_RBV CP")
field(OUT, "$(P)$(M).VELO")
record(ao, "$(INSTR)$(M):PushVELO2Field") {
field(DOL, "$(INSTR)$(M):VELO_RBV CP")
field(OUT, "$(INSTR)$(M).VELO")
field(OMSL, "closed_loop")
}
record(ai, "$(P)$(M):VBAS_RBV")
# This record pair reads the parameter library value for "motorVbasFromDriver_"
# and pushes it to the motor record field "VBAS". This can be used to read the lower speed limit
# from the hardware and correspondingly update the motor record from the driver.
# The implementation strategy is taken from https://epics.anl.gov/tech-talk/2022/msg00464.php.
# This record is coupled to the parameter library via motorVbasFromDriver_ -> MOTOR_VBAS_FROM_DRIVER.
record(ai, "$(INSTR)$(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")
field(FLNK, "$(INSTR)$(M):PushVBAS2Field")
}
record(ao, "$(INSTR)$(M):PushVBAS2Field") {
field(DOL, "$(INSTR)$(M):VBAS_RBV CP")
field(OUT, "$(INSTR)$(M).VBAS")
field(OMSL, "closed_loop")
}
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")
# This record pair reads the parameter library value for "motorVmaxFromDriver_"
# and pushes it to the motor record field "VMAX". This can be used to read the upper speed limit
# from the hardware and correspondingly update the motor record from the driver.
# The implementation strategy is taken from https://epics.anl.gov/tech-talk/2022/msg00464.php.
# This record is coupled to the parameter library via motorVmaxFromDriver_ -> MOTOR_VMAX_FROM_DRIVER.
record(ai, "$(INSTR)$(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")
field(FLNK, "$(INSTR)$(M):PushVMAX2Field")
}
record(ao, "$(INSTR)$(M):PushVMAX2Field") {
field(DOL, "$(INSTR)$(M):VMAX_RBV CP")
field(OUT, "$(INSTR)$(M).VMAX")
field(OMSL, "closed_loop")
}
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")
# This record pair reads the parameter library value for "motorAcclFromDriver_"
# and pushes it to the motor record field "ACCL". This can be used to read the acceleration
# from the hardware and correspondingly update the motor record from the driver.
# The implementation strategy is taken from https://epics.anl.gov/tech-talk/2022/msg00464.php.
# This record is coupled to the parameter library via motorAcclFromDriver_ -> MOTOR_ACCL_FROM_DRIVER.
record(ai, "$(INSTR)$(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
field(FLNK, "$(INSTR)$(M):PushACCL2Field")
}
record(ao, "$(INSTR)$(M):PushACCL2Field") {
field(DOL, "$(INSTR)$(M):ACCL_RBV CP")
field(OUT, "$(INSTR)$(M).ACCL")
field(OMSL, "closed_loop")
}

View File

@ -393,54 +393,6 @@ asynStatus sinqAxis::startMovTimeoutWatchdog() {
watchdogMovActive_ = true;
/*
The motor record resolution (index motorRecResolution_ in the parameter
library, MRES in the motor record) is NOT a conversion factor between
user units (e.g. mm) and motor units (e.g. encoder steps), but a scaling
factor defining the resolution of the position readback field RRBV. This
is due to an implementation detail inside EPICS described here:
https://epics.anl.gov/tech-talk/2018/msg00089.php
https://github.com/epics-modules/motor/issues/8
Basically, the position value in the parameter library is a double which
is then truncated to an integer in devMotorAsyn.c (because it was
originally meant for converting from engineering units to encoder steps,
which are by definition integer values). Therefore, if we want a
precision of 1 millimeter, we need to set MRES to 1. If we want one of
1 micrometer, we need to set MRES to 0.001. The readback value needs to
be multiplied with MRES to get the actual value.
In the driver, we use user units. Therefore, when we interact with the
parameter library, we need to account for MRES. This means:
- When writing position or speed to the parameter library, we divide the
value by the motor record resolution.
- When reading position or speed from the parameter library, we multiply
the value with the motor record resolution.
Index and motor record field are coupled as follows:
The parameter motorRecResolution_ is coupled to the field MRES of the
motor record in the following manner:
- In sinqMotor.db, the PV (motor_record_pv_name) MOTOR_REC_RESOLUTION
is defined as a copy of the field (motor_record_pv_name).MRES:
record(ao,"$(P)$(M):Resolution") {
field(DESC, "$(M) resolution")
field(DOL, "$(P)$(M).MRES CP MS")
field(OMSL, "closed_loop")
field(DTYP, "asynFloat64")
field(OUT, "@asyn($(PORT),$(ADDR))MOTOR_REC_RESOLUTION")
field(PREC, "$(PREC)")
}
- The PV name MOTOR_REC_RESOLUTION is coupled in asynMotorController.h
to the constant motorRecResolutionString
- ... which in turn is assigned to motorRecResolution_ in
asynMotorController.cpp This way of making the field visible to the
driver is described here:
https://epics.anl.gov/tech-talk/2020/msg00378.php This is a one-way
coupling, changes to the parameter library via setDoubleParam are NOT
transferred to (motor_record_pv_name).MRES or to
(motor_record_pv_name):Resolution.
NOTE: This function must not be called in the constructor (e.g. in order
to save the read result to the member variable earlier), since the
parameter library is updated at a later stage!

View File

@ -251,7 +251,7 @@ asynStatus sinqController::readInt32(asynUser *pasynUser, epicsInt32 *value) {
return asynSuccess;
} else if (pasynUser->reason == motorCanDisable_) {
// By default, motors cannot be disabled
*value = 0;
*value = 1;
return asynSuccess;
} else {
return asynMotorController::readInt32(pasynUser, value);

View File

@ -67,7 +67,7 @@ class epicsShareClass sinqController : public asynMotorController {
* @param value Read-out value
* @return asynStatus
*/
virtual asynStatus readInt32(asynUser *pasynUser, epicsInt32 *value);
asynStatus readInt32(asynUser *pasynUser, epicsInt32 *value);
/**
* @brief Error handling in case accessing the parameter library failed.