327 lines
15 KiB
Plaintext
Executable File
327 lines
15 KiB
Plaintext
Executable File
# SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
# 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. If not given, this is equal to M
|
|
# - 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".
|
|
# - RTRY: The maximum number of times the motor record will try again to move to
|
|
# the desired position. When the retry limit is reached, the motor record will
|
|
# declare the motion finished. If the desired position was not reached, the
|
|
# field MISS will be set to 1 and NICOS will emit a warning "Did not reach
|
|
# target position". If this value is set to 0, the retry deadband is never
|
|
# applied and therefore MISS will always be 0. The error message "Did not reach
|
|
# target position" will therefore never appear.
|
|
# - RDBD: Retry deadband: When the motor has finished a complete motion,
|
|
# possibly including backlash takeout, the motor record will compare its current
|
|
# position with the desired position. If the magnitude of the difference is
|
|
# greater than RDBD, the motor will try again, as if the user had requested a
|
|
# move from the now current position to the desired position. Only a limited
|
|
# number of retries will be performed (see RTRY). If the given value is smaller
|
|
# than MRES, it is set to MRES. In this version of the record, we set RDBD to a
|
|
# very high value in order to suppress both retries and the NTM (new target
|
|
# monitor) logic from issuing stop commands during overshoots (see
|
|
# https://epics.anl.gov/bcda/synApps/motor/motorRecord.html#Fields_misc).
|
|
record(motor,"$(INSTR)$(M)")
|
|
{
|
|
field(DESC,"$(DESC=$(M))")
|
|
field(DTYP,"asynMotor")
|
|
field(DIR,"$(DIR=Pos)")
|
|
field(OUT,"@asyn($(CONTROLLER),$(AXIS))")
|
|
field(MRES,"$(MRES)")
|
|
field(EGU,"$(EGU)")
|
|
field(INIT,"")
|
|
field(PINI,"NO")
|
|
field(DHLM, "$(DHLM=0)")
|
|
field(DLLM, "$(DLLM=0)")
|
|
field(TWV,"1")
|
|
field(RTRY,"0")
|
|
field(RDBD, "$(RDBD=10e300)") # Suppress retries and overshoot stop commands
|
|
field(BDST, "0")
|
|
field(RMOD,"3") # Retry mode 3 ("In-Position"): This suppresses any retries from the motor record.
|
|
}
|
|
|
|
# This PV reads out the 10th bit of the MSTA field of the motor record, which
|
|
# is the "motorStatusProblem_" bit.
|
|
record(calc, "$(INSTR)$(M):StatusProblem")
|
|
{
|
|
field(INPA, "$(INSTR)$(M).MSTA CP")
|
|
field(CALC, "A >> 9")
|
|
}
|
|
|
|
# If the value of this PV is 0, the according axis is currently disconnected from the controller.
|
|
# Trying to give commands to a disconnected axis will result in an error message in the IOC shell
|
|
# This record is coupled to the parameter library via motorConnected_ -> MOTOR_CONNECTED.
|
|
record(longin, "$(INSTR)$(M):Connected")
|
|
{
|
|
field(DTYP, "asynInt32")
|
|
field(INP, "@asyn($(CONTROLLER),$(AXIS)) MOTOR_CONNECTED")
|
|
field(SCAN, "I/O Intr")
|
|
field(PINI, "NO")
|
|
field(VAL, "1")
|
|
}
|
|
|
|
# Call the reset function of the corresponding sinqAxis
|
|
# This record is coupled to the parameter library via motorReset_ -> MOTOR_RESET.
|
|
record(longout, "$(INSTR)$(M):Reset") {
|
|
field(DTYP, "asynInt32")
|
|
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) MOTOR_RESET")
|
|
field(PINI, "NO")
|
|
}
|
|
|
|
# This PV allows force-stopping the motor record from within the driver by setting
|
|
# the motorForceStop_ value in the parameter library to 1. It should be reset to 0 by the driver afterwards.
|
|
# The implementation strategy is taken from https://epics.anl.gov/tech-talk/2022/msg00464.php.
|
|
# This record is coupled to the parameter library via motorForceStop_ -> MOTOR_FORCE_STOP.
|
|
record(longin, "$(INSTR)$(M):StopRBV")
|
|
{
|
|
field(DTYP, "asynInt32")
|
|
field(INP, "@asyn($(CONTROLLER),$(AXIS)) MOTOR_FORCE_STOP")
|
|
field(SCAN, "I/O Intr")
|
|
field(FLNK, "$(INSTR)$(M):Stop2Field")
|
|
}
|
|
record(longout, "$(INSTR)$(M):Stop2Field") {
|
|
field(DOL, "$(INSTR)$(M):StopRBV NPP")
|
|
field(OUT, "$(INSTR)$(M).STOP")
|
|
field(OMSL, "closed_loop")
|
|
}
|
|
|
|
# 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")
|
|
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")
|
|
field(NELM, "$(MSGTEXTSIZE=200)") # Should be the same as MAXBUF in the driver code
|
|
field(SCAN, "I/O Intr")
|
|
}
|
|
|
|
# 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")
|
|
}
|
|
|
|
# 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):EnableRBV") {
|
|
field(DTYP, "asynInt32")
|
|
field(INP, "@asyn($(CONTROLLER),$(AXIS),1) MOTOR_ENABLE_RBV")
|
|
field(PINI, "NO")
|
|
field(SCAN, "I/O Intr")
|
|
}
|
|
|
|
# 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")
|
|
}
|
|
|
|
# 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")
|
|
field(ASG, "READONLY") # Field is initialized during IOC startup
|
|
field(VAL, "$(CANSETSPEED=0)")
|
|
}
|
|
|
|
# If this PV has a value other than 0, adaptive polling for this axis is enabled.
|
|
# The standard motor record behaviour is to poll all axis with the busy / move poll
|
|
# period if at least one of the axes is moving. Adaptive polling changes this so
|
|
# that only axes which were moving in the last poll are polled with the busy / move poll
|
|
# period and all other axes are polled with the idle poll period.
|
|
record(longout, "$(INSTR)$(M):AdaptivePolling") {
|
|
field(DTYP, "asynInt32")
|
|
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) ADAPTIVE_POLLING")
|
|
field(PINI, "YES")
|
|
field(VAL, "$(ADAPTPOLL=1)")
|
|
}
|
|
|
|
# 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.
|
|
# 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")
|
|
field(ASG, "READONLY") # Field is initialized during IOC startup
|
|
field(VAL, "$(LIMITSOFFSET=0)")
|
|
}
|
|
|
|
# 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(VAL, "$(DHLM=0)")
|
|
field(INP, "@asyn($(CONTROLLER),$(AXIS)) MOTOR_HIGH_LIMIT_FROM_DRIVER")
|
|
field(SCAN, "I/O Intr")
|
|
field(FLNK, "$(INSTR)$(M):PushDHLM2Field")
|
|
field(PINI, "NO")
|
|
}
|
|
record(ao, "$(INSTR)$(M):PushDHLM2Field") {
|
|
field(DOL, "$(INSTR)$(M):DHLM_RBV NPP")
|
|
field(OUT, "$(INSTR)$(M).DHLM")
|
|
field(OMSL, "closed_loop")
|
|
field(PINI, "NO")
|
|
}
|
|
|
|
# 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(VAL, "$(DLLM=0)")
|
|
field(INP, "@asyn($(CONTROLLER),$(AXIS)) MOTOR_LOW_LIMIT_FROM_DRIVER")
|
|
field(SCAN, "I/O Intr")
|
|
field(FLNK, "$(INSTR)$(M):PushDLLM2Field")
|
|
field(PINI, "NO")
|
|
}
|
|
record(ao, "$(INSTR)$(M):PushDLLM2Field") {
|
|
field(DOL, "$(INSTR)$(M):DLLM_RBV NPP")
|
|
field(OUT, "$(INSTR)$(M).DLLM")
|
|
field(OMSL, "closed_loop")
|
|
field(PINI, "NO")
|
|
}
|
|
|
|
# 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, "$(INSTR)$(M):PushVELO2Field")
|
|
}
|
|
record(ao, "$(INSTR)$(M):PushVELO2Field") {
|
|
field(DOL, "$(INSTR)$(M):VELO_RBV NPP")
|
|
field(OUT, "$(INSTR)$(M).VELO")
|
|
field(OMSL, "closed_loop")
|
|
}
|
|
|
|
# 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, "$(INSTR)$(M):PushVBAS2Field")
|
|
}
|
|
record(ao, "$(INSTR)$(M):PushVBAS2Field") {
|
|
field(DOL, "$(INSTR)$(M):VBAS_RBV NPP")
|
|
field(OUT, "$(INSTR)$(M).VBAS")
|
|
field(OMSL, "closed_loop")
|
|
}
|
|
|
|
# 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, "$(INSTR)$(M):PushVMAX2Field")
|
|
}
|
|
record(ao, "$(INSTR)$(M):PushVMAX2Field") {
|
|
field(DOL, "$(INSTR)$(M):VMAX_RBV NPP")
|
|
field(OUT, "$(INSTR)$(M).VMAX")
|
|
field(OMSL, "closed_loop")
|
|
}
|
|
|
|
# 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, "$(INSTR)$(M):PushACCL2Field")
|
|
}
|
|
record(ao, "$(INSTR)$(M):PushACCL2Field") {
|
|
field(DOL, "$(INSTR)$(M):ACCL_RBV NPP")
|
|
field(OUT, "$(INSTR)$(M).ACCL")
|
|
field(OMSL, "closed_loop")
|
|
}
|
|
|
|
# Read out the encoder type in human-readable form. The output numbers are ASCII
|
|
# codes and can be converted to chars in order to get the encoder type.
|
|
# EPICS prepends the ASCII code with 80
|
|
# The following encoder types are defined:
|
|
# - "Absolute encoder" (array 80 65 98 115 111 108 117 116 101 32 101 110 99 111 100 101 114)
|
|
# - "Incremental encoder" (array 80 73 110 99 114 101 109 101 110 116 97 108 32 101 110 99 111 100 101 114)
|
|
# This record is coupled to the parameter library via encoderType -> ENCODER_TYPE.
|
|
record(waveform, "$(INSTR)$(M):EncoderType") {
|
|
field(DTYP, "asynOctetRead")
|
|
field(INP, "@asyn($(CONTROLLER),$(AXIS),1) ENCODER_TYPE")
|
|
field(FTVL, "CHAR")
|
|
field(NELM, "80")
|
|
field(SCAN, "I/O Intr")
|
|
}
|