Files
dev/script/_Lib/epics/epicsMotor.py
2018-04-17 12:05:48 +02:00

358 lines
14 KiB
Python

"""
This module provides support for the EPICS motor record.
Author: Mark Rivers
Created: Sept. 16, 2002
Modifications:
"""
import time
import epicsPV
class epicsMotor:
"""
This module provides a class library for the EPICS motor record.
It uses the epicsPV class, which is in turn a subclass of CaChannel.
Virtual attributes:
These attributes do not appear in the dictionary for this class, but
are implemented with the __getattr__ and __setattr__ methods. They
simply do getw() or putw(value) to the appropriate motor record fields.
All attributes can be both read and written unless otherwise noted.
Attribute Description Field
--------- ----------------------- -----
slew_speed Slew speed or velocity .VELO
base_speed Base or starting speed .VBAS
acceleration Acceleration time (sec) .ACCL
description Description of motor .DESC
resolution Resolution (units/step) .MRES
high_limit High soft limit (user) .HLM
low_limit Low soft limit (user) .LLM
dial_high_limit High soft limit (dial) .DHLM
dial_low_limit Low soft limit (dial) .DLLM
backlash Backlash distance .BDST
offset Offset from dial to user .OFF
done_moving 1=Done, 0=Moving, read-only .DMOV
Exceptions:
The check_limits() method raises an "epicsMotorException" if a soft limit
or hard limit is detected. The move() and wait() methods call
check_limits() before they return, unless they are called with the
ignore_limits=1 keyword set.
Example use:
from epicsMotor import *
m=epicsMotor('13BMD:m38')
m.move(10) # Move to position 10 in user coordinates
m.move(100, dial=1) # Move to position 100 in dial coordinates
m.move(1, step=1, relative=1) # Move 1 step relative to current position
m.wait() # Wait for motor to stop moving
m.wait(start=1) # Wait for motor to start moving
m.wait(start=1, stop=1) # Wait for motor to start, then to stop
m.stop() # Stop moving immediately
high = m.high_limit # Get the high soft limit in user coordinates
m.dial_high_limit = 100 # Set the high limit to 100 in dial coodinates
speed = m.slew_speed # Get the slew speed
m.acceleration = 0.1 # Set the acceleration to 0.1 seconds
p=m.get_position() # Get the desired motor position in user coordinates
p=m.get_position(dial=1) # Get the desired motor position in dial coordinates
p=m.get_position(readback=1) # Get the actual position in user coordinates
p=m.get_position(readback=1, step=1) Get the actual motor position in steps
p=m.set_position(100) # Set the current position to 100 in user coordinates
# Puts motor in Set mode, writes value, puts back in Use mode.
p=m.set_position(10000, step=1) # Set the current position to 10000 steps
"""
def __init__(self, name):
"""
Creates a new epicsMotor instance.
Inputs:
name:
The name of the EPICS motor record without any trailing period or field
name.
Example:
m=epicsMotor('13BMD:m38')
"""
self.pvs = {'val' : epicsPV.epicsPV(name+'.VAL', wait=0),
'dval': epicsPV.epicsPV(name+'.DVAL', wait=0),
'rval': epicsPV.epicsPV(name+'.RVAL', wait=0),
'rlv' : epicsPV.epicsPV(name+'.RLV', wait=0),
'rbv' : epicsPV.epicsPV(name+'.RBV', wait=0),
'drbv': epicsPV.epicsPV(name+'.DRBV', wait=0),
'rrbv': epicsPV.epicsPV(name+'.RRBV', wait=0),
'dmov': epicsPV.epicsPV(name+'.DMOV', wait=0),
'stop': epicsPV.epicsPV(name+'.STOP', wait=0),
'velo': epicsPV.epicsPV(name+'.VELO', wait=0),
'vbas': epicsPV.epicsPV(name+'.VBAS', wait=0),
'accl': epicsPV.epicsPV(name+'.ACCL', wait=0),
'desc': epicsPV.epicsPV(name+'.DESC', wait=0),
'mres': epicsPV.epicsPV(name+'.MRES', wait=0),
'hlm': epicsPV.epicsPV(name+'.HLM', wait=0),
'llm': epicsPV.epicsPV(name+'.LLM', wait=0),
'dhlm': epicsPV.epicsPV(name+'.DHLM', wait=0),
'dllm': epicsPV.epicsPV(name+'.DLLM', wait=0),
'bdst': epicsPV.epicsPV(name+'.BDST', wait=0),
'set': epicsPV.epicsPV(name+'.SET', wait=0),
'lvio': epicsPV.epicsPV(name+'.LVIO', wait=0),
'lls': epicsPV.epicsPV(name+'.LLS', wait=0),
'hls': epicsPV.epicsPV(name+'.HLS', wait=0),
'off': epicsPV.epicsPV(name+'.OFF', wait=0)
}
# Wait for all PVs to connect
self.pvs['val'].pend_io()
def move(self, value, relative=0, dial=0, step=0, ignore_limits=0):
"""
Moves a motor to an absolute position or relative to the current position
in user, dial or step coordinates.
Inputs:
value:
The absolute position or relative amount of the move
Keywords:
relative:
Set relative=1 to move relative to current position.
The default is an absolute move.
dial:
Set dial=1 if "value" is in dial coordinates.
The default is user coordinates.
step:
Set step=1 if "value" is in steps.
The default is user coordinates.
ignore_limits:
Set ignore_limits=1 to suppress raising exceptions
if the move results in a soft or hard limit violation.
Notes:
The "step" and "dial" keywords are mutually exclusive.
The "relative" keyword can be used in user, dial or step
coordinates.
Examples:
m=epicsMotor('13BMD:m38')
m.move(10) # Move to position 10 in user coordinates
m.move(100, dial=1) # Move to position 100 in dial coordinates
m.move(2, step=1, relative=1) # Move 2 steps
"""
if (dial != 0):
# Position in dial coordinates
if (relative != 0):
current = self.get_position(dial=1)
self.pvs['dval'].putw(current+value)
else:
self.pvs['dval'].putw(value)
elif (step != 0):
# Position in steps
if (relative != 0):
current = self.get_position(step=1)
self.pvs['rval'].putw(current + value)
else:
self.pvs['rval'].putw(value)
else:
# Position in user coordinates
if (relative != 0):
self.pvs['rlv'].putw(value)
else:
self.pvs['val'].putw(value)
# Check for limit violations
if (ignore_limits == 0): self.check_limits()
def check_limits(self):
limit = self.pvs['lvio'].getw()
if (limit!=0):
raise epicsMotorException('Soft limit violation')
limit = self.pvs['lls'].getw()
if (limit!=0):
raise epicsMotorException('Low hard limit violation')
limit = self.pvs['hls'].getw()
if (limit!=0):
raise epicsMotorException('High hard limit violation')
def stop(self):
"""
Immediately stops a motor from moving by writing 1 to the .STOP field.
Examples:
m=epicsMotor('13BMD:m38')
m.move(10) # Move to position 10 in user coordinates
m.stop() # Stop motor
"""
self.pvs['stop'].putw(1)
def get_position(self, dial=0, readback=0, step=0):
"""
Returns the target or readback motor position in user, dial or step
coordinates.
Keywords:
readback:
Set readback=1 to return the readback position in the
desired coordinate system. The default is to return the
target position of the motor.
dial:
Set dial=1 to return the position in dial coordinates.
The default is user coordinates.
step:
Set step=1 to return the position in steps.
The default is user coordinates.
Notes:
The "step" and "dial" keywords are mutually exclusive.
The "readback" keyword can be used in user, dial or step
coordinates.
Examples:
m=epicsMotor('13BMD:m38')
m.move(10) # Move to position 10 in user coordinates
p=m.get_position(dial=1) # Read the target position in
# dial coordinates
p=m.get_position(readback=1, step=1) # Read the actual position in
# steps
"""
if (dial != 0):
if (readback != 0):
return self.pvs['drbv'].getw()
else:
return self.pvs['dval'].getw()
elif (step != 0):
if (readback != 0):
return self.pvs['rrbv'].getw()
else:
return self.pvs['rval'].getw()
else:
if (readback != 0):
return self.pvs['rbv'].getw()
else:
return self.pvs['val'].getw()
def set_position(self, position, dial=0, step=0):
"""
Sets the motor position in user, dial or step coordinates.
Inputs:
position:
The new motor position
Keywords:
dial:
Set dial=1 to set the position in dial coordinates.
The default is user coordinates.
step:
Set step=1 to set the position in steps.
The default is user coordinates.
Notes:
The "step" and "dial" keywords are mutually exclusive.
Examples:
m=epicsMotor('13BMD:m38')
m.set_position(10, dial=1) # Set the motor position to 10 in
# dial coordinates
m.set_position(1000, step=1) # Set the motor position to 1000 steps
"""
# Put the motor in "SET" mode
self.pvs['set'].putw(1)
if (dial != 0):
self.pvs['dval'].putw(position)
elif (step != 0):
self.pvs['rval'].putw(position)
else:
self.pvs['val'].putw(position)
# Put the motor back in "Use" mode
self.pvs['set'].putw(0)
def wait(self, start=0, stop=0, poll=0.01, ignore_limits=0):
"""
Waits for the motor to start moving and/or stop moving.
Keywords:
start:
Set start=1 to wait for the motor to start moving.
stop:
Set stop=1 to wait for the motor to stop moving.
poll:
Set this keyword to the time to wait between reading the
.DMOV field of the record to see if the motor is moving.
The default is 0.01 seconds.
ignore_limits:
Set ignore_limits=1 to suppress raising an exception if a soft or
hard limit is detected
Notes:
If neither the "start" nor "stop" keywords are set then "stop"
is set to 1, so the routine waits for the motor to stop moving.
If only "start" is set to 1 then the routine only waits for the
motor to start moving.
If both "start" and "stop" are set to 1 then the routine first
waits for the motor to start moving, and then to stop moving.
Examples:
m=epicsMotor('13BMD:m38')
m.move(100) # Move to position 100
m.wait(start=1, stop=1) # Wait for the motor to start moving
# and then to stop moving
"""
if (start == 0) and (stop == 0): stop=1
if (start != 0):
while(1):
done = self.pvs['dmov'].getw()
if (done != 1): break
time.sleep(poll)
if (stop != 0):
while(1):
done = self.pvs['dmov'].getw()
if (done != 0): break
time.sleep(poll)
if (ignore_limits == 0): self.check_limits()
def __getattr__(self, attrname):
if (attrname == 'slew_speed'): return self.pvs['velo'].getw()
elif (attrname == 'base_speed'): return self.pvs['vbas'].getw()
elif (attrname == 'acceleration'): return self.pvs['accl'].getw()
elif (attrname == 'description'): return self.pvs['desc'].getw()
elif (attrname == 'resolution'): return self.pvs['mres'].getw()
elif (attrname == 'high_limit'): return self.pvs['hlm'].getw()
elif (attrname == 'low_limit'): return self.pvs['llm'].getw()
elif (attrname == 'dial_high_limit'): return self.pvs['dhlm'].getw()
elif (attrname == 'dial_low_limit'): return self.pvs['dllm'].getw()
elif (attrname == 'backlash'): return self.pvs['bdst'].getw()
elif (attrname == 'offset'): return self.pvs['off'].getw()
elif (attrname == 'done_moving'): return self.pvs['dmov'].getw()
else: raise AttributeError(attrname)
def __setattr__(self, attrname, value):
if (attrname == 'pvs'): self.__dict__[attrname]=value
elif (attrname == 'slew_speed'): self.pvs['velo'].putw(value)
elif (attrname == 'base_speed'): self.pvs['vbas'].putw(value)
elif (attrname == 'acceleration'): self.pvs['accl'].putw(value)
elif (attrname == 'description'): self.pvs['desc'].putw(value)
elif (attrname == 'resolution'): self.pvs['mres'].putw(value)
elif (attrname == 'high_limit'): self.pvs['hlm'].putw(value)
elif (attrname == 'low_limit'): self.pvs['llm'].putw(value)
elif (attrname == 'dial_high_limit'): self.pvs['dhlm'].putw(value)
elif (attrname == 'dial_low_limit'): self.pvs['dllm'].putw(value)
elif (attrname == 'backlash'): self.pvs['bdst'].putw(value)
elif (attrname == 'offset'): self.pvs['off'].putw(value)
else: raise AttributeError(attrname)
class epicsMotorException(Exception):
def __init__(self, args=None):
self.args=args