316 lines
10 KiB
Python
316 lines
10 KiB
Python
import subprocess
|
|
from threading import Thread
|
|
from epics import PV, ca
|
|
import time
|
|
from ..eco_epics import device
|
|
from ..eco_epics.device import Device
|
|
|
|
_guiTypes = ['xdm']
|
|
|
|
def _keywordChecker(kw_key_list_tups):
|
|
for tkw,tkey,tlist in kw_key_list_tups:
|
|
assert tkey in tlist, "Keyword %s should be one of %s"%(tkw,tlist)
|
|
|
|
class SmarActException(Exception):
|
|
""" raised to indicate a problem with a smartact"""
|
|
def __init__(self, msg, *args):
|
|
Exception.__init__(self, *args)
|
|
self.msg = msg
|
|
def __str__(self):
|
|
return str(self.msg)
|
|
|
|
class SmarAct(Device):
|
|
_extras = {'disabled':'_able.VAL', }
|
|
_init_list = ('VAL', 'DESC', 'RTYP')
|
|
_nonpvs = ('_prefix', '_pvs', '_delim', '_init', '_init_list', '_alias', '_extras')
|
|
def __init__(self, name=None, timeout=3.0, record=None):
|
|
if name is None:
|
|
raise SmarActException("must supply SmarAct name")
|
|
|
|
if name.endswith('.VAL'):
|
|
name = name[:-4]
|
|
if name.endswith('.'):
|
|
name = name[:-1]
|
|
|
|
self._prefix = name
|
|
self._record = record
|
|
self._callbacks = {}
|
|
|
|
device.Device.__init__(self, name, delim='.',
|
|
attrs=self._init_list,
|
|
timeout=timeout)
|
|
|
|
|
|
# for key, val in self._extras.items():
|
|
# pvname = "%s%s" % (name, val)
|
|
# self.add_pv(pvname, attr=key)
|
|
|
|
# self.put('disabled', 0)
|
|
|
|
class SmarActRecord:
|
|
def __init__(self,Id, name=None, elog=None):
|
|
self.Id = Id
|
|
self._drive = SmarAct(Id+':DRIVE')
|
|
self._rbv = SmarAct(Id+':MOTRBV')
|
|
self._hlm = SmarAct(Id+':HLM')
|
|
self._llm = SmarAct(Id+':LLM')
|
|
self._status = SmarAct(Id+':STATUS')
|
|
self._set_pos = SmarAct(Id+':SET_POS')
|
|
self._stop = SmarAct(Id+':STOP')
|
|
self._hold = SmarAct(Id+':HOLD')
|
|
self._twv = SmarAct(Id+':TWV')
|
|
self._elog = elog
|
|
self.name = name
|
|
self.units = self._drive.get('EGU')
|
|
|
|
# Conventional methods and properties for all Adjustable objects
|
|
def changeTo(self, value, hold=False, check=True):
|
|
""" Adjustable convention"""
|
|
|
|
mover = lambda value: self.move(\
|
|
value, ignore_limits=(not check),
|
|
wait=True)
|
|
return Changer(
|
|
target=value,
|
|
parent=self,
|
|
mover=mover,
|
|
hold=hold,
|
|
stopper=self._stop.put('PROC', 1))
|
|
|
|
def stop(self):
|
|
""" Adjustable convention"""
|
|
try:
|
|
self._currentChange.stop()
|
|
except:
|
|
self._stop.put('VAL',1)
|
|
pass
|
|
|
|
def within_limits(self, val):
|
|
""" returns whether a value for a motor is within drive limits"""
|
|
return (val <= self._hlm.get('VAL') and val >= self._llm.get('VAL'))
|
|
|
|
def move(self, val, relative=False, wait=False, timeout=300.0, ignore_limits=False, confirm_move=False):
|
|
""" moves smaract drive to position
|
|
|
|
arguments:
|
|
==========
|
|
val value to move to (float) [Must be provided]
|
|
relative move relative to current position (T/F) [F]
|
|
wait whether to wait for move to complete (T/F) [F]
|
|
ignore_limits try move without regard to limits (T/F) [F]
|
|
confirm_move try to confirm that move has begun (T/F) [F]
|
|
timeout max time for move to complete (in seconds) [300]
|
|
|
|
return values:
|
|
-13 : invalid value (cannot convert to float). Move not attempted.
|
|
-12 : target value outside soft limits. Move not attempted.
|
|
-11 : drive PV is not connected: Move not attempted.
|
|
-8 : move started, but timed-out.
|
|
-7 : move started, timed-out, but appears done.
|
|
-5 : move started, unexpected return value from PV.put()
|
|
-4 : move-with-wait finished, soft limit violation seen
|
|
-3 : move-with-wait finished, hard limit violation seen
|
|
0 : move-with-wait finish OK.
|
|
0 : move-without-wait executed, not cpmfirmed
|
|
1 : move-without-wait executed, move confirmed
|
|
3 : move-without-wait finished, hard limit violation seen
|
|
4 : move-without-wait finished, soft limit violation seen
|
|
|
|
"""
|
|
NONFLOAT, OUTSIDE_LIMITS, UNCONNECTED = -13, -12, -11
|
|
TIMEOUT = -8
|
|
UNKNOWN_ERROR = -5
|
|
DONE_OK = 0
|
|
MOVE_BEGUN, MOVE_BEGUN_CONFIRMED = 0, 1
|
|
try:
|
|
val = float(val)
|
|
except TypeError:
|
|
return NONFLOAT
|
|
|
|
if relative:
|
|
val += self._drive.get('VAL')
|
|
|
|
# Check for limit violations
|
|
if not ignore_limits:
|
|
if not self.within_limits(val):
|
|
return OUTSIDE_LIMITS
|
|
|
|
stat = self._drive.put('VAL', val, wait=wait, timeout=timeout)
|
|
if stat is None:
|
|
return UNCONNECTED
|
|
|
|
if wait and stat == -1:
|
|
return TIMEOUT
|
|
|
|
if 1 == stat:
|
|
s0 = self._status.get('VAL')
|
|
s1 = s0
|
|
t0 = time.time()
|
|
t1 = t0 + min(10.0, timeout) # should be moving by now
|
|
thold = self._hold.get('VAL') * 0.001 + t0
|
|
tout = t0 + timeout
|
|
if wait or confirm_move:
|
|
while time.time() <= thold and s1 == 3:
|
|
ca.poll(evt=1.e-2)
|
|
s1 = self._status.get('VAL')
|
|
while time.time() <= t1 and s1 == 0:
|
|
ca.poll(evt=1.e-2)
|
|
s1 = self._status.get('VAL')
|
|
if s1 == 4:
|
|
if wait:
|
|
while time.time() <= tout and s1 == 4:
|
|
ca.poll(evt=1.e-2)
|
|
s1 = self._status.get('VAL')
|
|
if s1 == 3 or s1 == 4:
|
|
if time.time() > tout:
|
|
return TIMEOUT
|
|
else:
|
|
twv = abs(self._twv.get('VAL'))
|
|
while s1==3 and time.time()<=tout and abs(self._rbv.get('VAL')-val)>=twv:
|
|
ca.poll(evt=1.e-2)
|
|
return DONE_OK
|
|
else:
|
|
return MOVE_BEGUN_CONFIRMED
|
|
elif time.time() > tout:
|
|
return TIMEOUT
|
|
else:
|
|
return UNKNOWN_ERROR
|
|
else:
|
|
return MOVE_BEGUN
|
|
return UNKNOWN_ERROR
|
|
|
|
def get_current_value(self,readback=True):
|
|
if readback:
|
|
return self._rbv.get('VAL')
|
|
else :
|
|
return self._drive.get('VAL')
|
|
|
|
def set_current_value(self,value):
|
|
return self._set_pos.put('VAL',value)
|
|
|
|
def get_precision(self):
|
|
""" Adjustable convention"""
|
|
pass
|
|
|
|
def set_precision(self):
|
|
""" Adjustable convention"""
|
|
pass
|
|
|
|
precision = property(get_precision,set_precision)
|
|
|
|
def set_speed(self):
|
|
""" Adjustable convention"""
|
|
pass
|
|
def get_speed(self):
|
|
""" Adjustable convention"""
|
|
pass
|
|
def set_speedMax(self):
|
|
""" Adjustable convention"""
|
|
pass
|
|
|
|
def get_moveDone(self):
|
|
""" Adjustable convention"""
|
|
pass
|
|
|
|
def set_limits(self, values, posType='user', relative_to_present=False):
|
|
""" Adjustable convention"""
|
|
if relative_to_present:
|
|
v = self.get_current_value()
|
|
values = [v-values[0],v-values[1]]
|
|
self._llm.put('VAL',values[0])
|
|
self._hlm.put('VAL',values[1])
|
|
|
|
def get_limits(self, posType='user'):
|
|
""" Adjustable convention"""
|
|
return self._llm.get('VAL'), self._hlm.get('VAL')
|
|
|
|
# def gui(self, guiType='xdm'):
|
|
# """ Adjustable convention"""
|
|
# cmd = ['caqtdm','-macro']
|
|
#
|
|
# cmd.append('\"P=%s:,M=%s\"'%tuple(self.Id.split(':')))
|
|
# #cmd.append('/sf/common/config/qt/motorx_more.ui')
|
|
# cmd.append('motorx_more.ui')
|
|
# #os.system(' '.join(cmd))
|
|
# return subprocess.Popen(' '.join(cmd),shell=True)
|
|
|
|
|
|
|
|
def mv(self,value):
|
|
self._currentChange = self.changeTo(value)
|
|
def wm(self,*args,**kwargs):
|
|
return self.get_current_value(*args,**kwargs)
|
|
def mvr(self,value,*args,**kwargs):
|
|
|
|
if(self.get_moveDone == 1):
|
|
startvalue = self.get_current_value(readback=True,*args,**kwargs)
|
|
else:
|
|
startvalue = self.get_current_value(readback=False,*args,**kwargs)
|
|
self._currentChange = self.changeTo(value+startvalue,*args,**kwargs)
|
|
def wait(self):
|
|
self._currentChange.wait()
|
|
|
|
|
|
# return string with motor value as variable representation
|
|
def __str__(self):
|
|
return "SmarAct is at %s"%(self.wm())
|
|
#return "SmarAct is at %s %s"%(self.wm(),self.units)
|
|
|
|
def __repr__(self):
|
|
return self.__str__()
|
|
|
|
def __call__(self,value):
|
|
self._currentChange = self.changeTo(value)
|
|
|
|
|
|
class SmarActDevice(SmarActRecord):
|
|
def __init__(self,Id,alias_namespace=None):
|
|
SmarActRecord.__init__(self, Id)
|
|
# self.Id = Id
|
|
#
|
|
# self.x = SmarActRecord(Id+':DRIVE')
|
|
|
|
class SmarActStage:
|
|
def __init__(self, axes, name):
|
|
self._keys = axes.keys()
|
|
for axis in self._keys:
|
|
self.__dict__[axis] = axes[axis]
|
|
self.name = name
|
|
|
|
def __str__(self):
|
|
return "SmarAct positions\n%s" % "\n".join(["%s: %s"%(key,self.__dict__[key].wm()) for key in self._keys])
|
|
|
|
def __repr__(self):
|
|
return str({key:self.__dict__[key].wm() for key in self._keys})
|
|
|
|
class Changer:
|
|
def __init__(self, target=None, parent=None, mover=None, hold=True, stopper=None):
|
|
self.target = target
|
|
self._mover = mover
|
|
self._stopper = stopper
|
|
self._thread = Thread(target=self._mover,args=(target,))
|
|
if not hold:
|
|
self._thread.start()
|
|
|
|
def wait(self):
|
|
self._thread.join()
|
|
|
|
def start(self):
|
|
self._thread.start()
|
|
|
|
def status(self):
|
|
if self._thread.ident is None:
|
|
return 'waiting'
|
|
else:
|
|
if self._isAlive:
|
|
return 'changing'
|
|
else:
|
|
return 'done'
|
|
def stop(self):
|
|
self._stopper()
|
|
|
|
|
|
|
|
|
|
|