reindent and some image tweeks

This commit is contained in:
2022-07-15 16:17:13 +02:00
parent d1653e9412
commit 079c9dbc4c
4 changed files with 3395 additions and 3316 deletions

View File

@@ -20,32 +20,32 @@ simulated = appsconf.get("simulate", False)
logger.info(f"configuring for endstation: {endstation.upper()}") logger.info(f"configuring for endstation: {endstation.upper()}")
if simulated: if simulated:
logger.warning("SIMULATION is ACTIVE") logger.warning("SIMULATION is ACTIVE")
css_file = inst_folder / "swissmx.css" css_file = inst_folder / "swissmx.css"
def font(name: str) -> str: def font(name: str) -> str:
p = Path(__file__).absolute().parent / "fonts" / name p = Path(__file__).absolute().parent / "fonts" / name
return str(p) return str(p)
def logo(size: int = 0) -> str: def logo(size: int = 0) -> str:
p = Path(__file__).absolute().parent / "logos" / "logo.png" p = Path(__file__).absolute().parent / "logos" / "logo.png"
if size: if size:
p = Path(__file__).absolute().parent / "logos" / f"tell_logo_{size}x{size}.png" p = Path(__file__).absolute().parent / "logos" / f"tell_logo_{size}x{size}.png"
return str(p) return str(p)
def option(key: str) -> bool: def option(key: str) -> bool:
try: try:
return settings.value(key, type=bool) return settings.value(key, type=bool)
except: except:
logger.error(f"option {key} not known") logger.error(f"option {key} not known")
return False return False
def toggle_option(key: str): def toggle_option(key: str):
v = settings.value(key, type=bool) v = settings.value(key, type=bool)
settings.setValue(key, not v) settings.setValue(key, not v)
settings.sync() settings.sync()

147
camera.py
View File

@@ -24,6 +24,20 @@ Best regards
Helge Helge
""" """
# In [2]: np.array(range(20))
# Out[2]:
# array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
# 17, 18, 19])
# In [3]: np.array(range(20)).reshape(4,5)
# Out[3]:
# array([[ 0, 1, 2, 3, 4],
# [ 5, 6, 7, 8, 9],
# [10, 11, 12, 13, 14],
# [15, 16, 17, 18, 19]])
# shape is (imgidx,h,w) w is the fast counting index
import logging import logging
_log = logging.getLogger(__name__) _log = logging.getLogger(__name__)
@@ -99,7 +113,7 @@ class epics_cam(object):
imgSeq=self._sim['imgSeq'] imgSeq=self._sim['imgSeq']
idx=self._sim['imgIdx'] idx=self._sim['imgIdx']
self._sim['imgIdx']=(idx + 1) % imgSeq.shape[0] self._sim['imgIdx']=(idx + 1) % imgSeq.shape[0]
_log.info('simulated idx:{}'.format(idx)) #_log.debug('simulated idx:{}'.format(idx))
self.pic=pic=imgSeq[idx] self.pic=pic=imgSeq[idx]
return pic return pic
try: try:
@@ -200,30 +214,54 @@ class epics_cam(object):
def sim_gen(self,sz=(1500,1000),t=100,mode=0): def sim_gen(self,sz=(1500,1000),t=100,mode=0):
'generate simulation data' 'generate simulation data'
_log.info('generate simulation images, mode:{}...'.format(mode)) if mode==0:
w,h=sz _log.info('generate {} pulsing wases simulation images, mode:{}...'.format(t,mode))
self._imgSeq=imgSeq=np.ndarray(shape=(t,h,w),dtype=np.uint16) w,h=sz
x = np.linspace(-5, 5, w) imgSeq=np.ndarray(shape=(t,h,w),dtype=np.uint16)
y = np.linspace(-5, 5, h) x = np.linspace(-5, 5, w)
# full coordinate arrays y = np.linspace(-5, 5, h)
xx, yy = np.meshgrid(x, y) # full coordinate arrays
xx, yy = np.meshgrid(x, y)
for i in range(t):
#imgSeq[i,:,:] = 100*np.sqrt(np.sin(xx+.1*i)**2 + np.sin(yy+.01*i)**2)#+xx*t+yy*t)
#imgSeq[i,:,:] = 100*np.sqrt(np.sin(xx+.1*i)**2 + np.sin((1+.1*np.sin(.2*i))*yy+.001*i**2)**2)#+xx*t+yy*t)
#imgSeq[i,:,:] = 100*np.sqrt(np.sin(xx+2*np.sin(i/t*2*np.pi))**2 + np.sin(yy)**2)
px=2*np.sin(i/t*2*np.pi)
fx=1
py=2*np.sin(i/t*2*np.pi)
fy=1+.3*np.sin(i/t*2*np.pi*2)
imgSeq[i,:,:] = 100*np.sqrt(np.sin(xx*fx+px)**2 + np.sin(yy*fy+py)**2)
#np.random.bytes(100)
wr=w//4
hr=h//4
imgSeq[:,0:hr,0:wr]+=np.random.randint(0,100,(t,hr,wr),dtype=np.uint16)
elif mode==1:
import glob,PIL.Image
path='/home/zamofing_t/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX/python/SwissMX/simCamImg/*.png'
_log.info('generate simulation images:{}...'.format(path))
glb=glob.glob(path)
img = PIL.Image.open(glb[0])
sz=img.size # (w,h)
imgSeq=np.ndarray(shape=(len(glb),sz[1],sz[0]),dtype=np.uint8) # shape is (n,h,w)
for i,fn in enumerate(glb):
img=PIL.Image.open(fn)
assert(img.mode=='L') # 8 bit grayscale
assert(sz==img.size)
imgSeq[i,:,:]=np.array(img.getdata()).reshape(sz[::-1])
f=np.array(((0,0,0,0,0),
(0,1,1,1,0),
(0,1,0,0,0),
(0,1,1,0,0),
(0,1,0,0,0),
(0,0,0,0,0),),imgSeq.dtype)
imgSeq[i,0:6,0:5]=f*255
for i in range(t):
#imgSeq[i,:,:] = 100*np.sqrt(np.sin(xx+.1*i)**2 + np.sin(yy+.01*i)**2)#+xx*t+yy*t)
#imgSeq[i,:,:] = 100*np.sqrt(np.sin(xx+.1*i)**2 + np.sin((1+.1*np.sin(.2*i))*yy+.001*i**2)**2)#+xx*t+yy*t)
#imgSeq[i,:,:] = 100*np.sqrt(np.sin(xx+2*np.sin(i/t*2*np.pi))**2 + np.sin(yy)**2)
px=2*np.sin(i/t*2*np.pi)
fx=1
py=2*np.sin(i/t*2*np.pi)
fy=1+.3*np.sin(i/t*2*np.pi*2)
imgSeq[i,:,:] = 100*np.sqrt(np.sin(xx*fx+px)**2 + np.sin(yy*fy+py)**2)
#np.random.bytes(100)
wr=w//4
hr=h//4
imgSeq[:,0:hr,0:wr]+=np.random.randint(0,100,(t,hr,wr),dtype=np.uint16)
self._sim['imgSeq']=imgSeq self._sim['imgSeq']=imgSeq
self._sim['imgIdx']=0 self._sim['imgIdx']=0
_log.info('dome') _log.info('done-> shape:{} dtype:{}'.format(imgSeq.shape,imgSeq.dtype))
def set_transformations(self,*args): def set_transformations(self,*args):
_log.error('OLD FUNCTION NOT IMPLEMENTED {}'.format(args)) _log.error('OLD FUNCTION NOT IMPLEMENTED {}'.format(args))
@@ -232,7 +270,9 @@ class epics_cam(object):
if __name__ == "__main__": if __name__ == "__main__":
import time, os, PIL.Image, platform, subprocess import time, os, PIL.Image, platform, subprocess
import argparse import argparse
logging.basicConfig(level=logging.DEBUG,format='%(levelname)s:%(module)s:%(lineno)d:%(funcName)s:%(message)s ') logging.basicConfig(level=logging.DEBUG,format='%(name)s:%(levelname)s:%(module)s:%(lineno)d:%(funcName)s:%(message)s ')
logging.getLogger('PIL').setLevel(logging.INFO)
def default_app_open(file): def default_app_open(file):
if platform.system() == 'Darwin': # macOS if platform.system() == 'Darwin': # macOS
@@ -248,6 +288,7 @@ if __name__ == "__main__":
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument("--ui", "-u", help="qt test", type=int, default=0) parser.add_argument("--ui", "-u", help="qt test", type=int, default=0)
parser.add_argument("--sim", "-s", help="simulation mode", type=int, default=None) parser.add_argument("--sim", "-s", help="simulation mode", type=int, default=None)
parser.add_argument("--delay", "-d", help="delay in simulation mode", type=float, default=None)
parser.add_argument("--prefix","-p",help="PV prefix for images: default=%(default)s",type=str,default="SARES30-CAMS156-SMX-OAV",) parser.add_argument("--prefix","-p",help="PV prefix for images: default=%(default)s",type=str,default="SARES30-CAMS156-SMX-OAV",)
args = parser.parse_args() args = parser.parse_args()
@@ -328,7 +369,11 @@ if __name__ == "__main__":
(0,1,0,0,0), (0,1,0,0,0),
(0,0,0,0,0),),pic.dtype) (0,0,0,0,0),),pic.dtype)
pic[0:6,0:5]=f*pic.max() pic[0:6,0:5]=f*pic.max()
imv.setImage(pic, autoRange=False, autoLevels=False) if args.ui==1:
img.setImage(pic)
else:
imv.setImage(pic, autoRange=False, autoLevels=False)
def new_frame_sim_cb(self,arl=False): def new_frame_sim_cb(self,arl=False):
imgSeq =self._sim['imgSeq'] imgSeq =self._sim['imgSeq']
@@ -338,9 +383,15 @@ if __name__ == "__main__":
self._sim['imgIdx']=(idx+1) % imgSeq.shape[0] self._sim['imgIdx']=(idx+1) % imgSeq.shape[0]
#_log.info('simulated idx:{}'.format(idx)) #_log.info('simulated idx:{}'.format(idx))
pic = imgSeq[idx] pic = imgSeq[idx]
imv.setImage(pic, autoRange=arl, autoLevels=arl) if args.ui==1:
img.setImage(pic)
else:
imv.setImage(pic, autoRange=arl, autoLevels=arl)
QtCore.QTimer.singleShot(1, self.new_frame_sim_cb) if args.delay:
QtCore.QTimer.singleShot(int(1000*args.delay), self.new_frame_sim_cb)
else:
QtCore.QTimer.singleShot(0, self.new_frame_sim_cb)
now = ptime.time() now = ptime.time()
fps2 = 1.0 / (now - udt) fps2 = 1.0 / (now - udt)
self._sim['updateTime'] = now self._sim['updateTime'] = now
@@ -358,13 +409,34 @@ if __name__ == "__main__":
app = QtGui.QApplication([]) app = QtGui.QApplication([])
## Create window with ImageView widget if args.ui==1:
win = QtGui.QMainWindow() win = pg.GraphicsLayoutWidget()
win.resize(800,800) win.show() ## show widget alone in its own window
imv = pg.ImageView() win.setWindowTitle('pyqtgraph example: ImageItem')
win.setCentralWidget(imv) view = win.addViewBox(invertY=True)
win.show()
win.setWindowTitle('pyqtgraph example: ImageView') ## lock the aspect ratio so pixels are always square
view.setAspectLocked(True)
## Create image item https://pyqtgraph.readthedocs.io/en/latest/graphicsItems/imageitem.html
img = pg.ImageItem(border='g')
#tr = QtGui.QTransform() # prepare ImageItem transformation:
#tr.scale(6.0, 3.0) # scale horizontal and vertical axes
#tr.translate(-1.5, -1.5) # move 3x3 image to locate center at axis origin
#img.setTransform(tr) # assign transform
view.addItem(img)
## Set initial view bounds
view.setRange(QtCore.QRectF(0, 0, 600, 600))
else:
## Create window with ImageView widget
win = QtGui.QMainWindow()
win.resize(800,800)
imv = pg.ImageView()
win.setCentralWidget(imv)
win.show()
win.setWindowTitle('pyqtgraph example: ImageView')
## Display the data and assign each frame a time value from 1.0 to 3.0 ## Display the data and assign each frame a time value from 1.0 to 3.0
cam = UIcamera(prefix=args.prefix) cam = UIcamera(prefix=args.prefix)
@@ -383,10 +455,11 @@ if __name__ == "__main__":
cam._sim['updateTime'] = ptime.time() cam._sim['updateTime'] = ptime.time()
cam.new_frame_sim_cb(arl=True) cam.new_frame_sim_cb(arl=True)
## Set a custom color map if args.ui==2:
colors = [(0, 0, 0),(45, 5, 61),(84, 42, 55),(150, 87, 60),(208, 171, 141),(255, 255, 255)] ## Set a custom color map
cmap = pg.ColorMap(pos=np.linspace(0.0, 1.0, 6), color=colors) colors = [(0, 0, 0),(45, 5, 61),(84, 42, 55),(150, 87, 60),(208, 171, 141),(255, 255, 255)]
imv.setColorMap(cmap) cmap = pg.ColorMap(pos=np.linspace(0.0, 1.0, 6), color=colors)
imv.setColorMap(cmap)
## Start Qt event loop unless running in interactive mode. ## Start Qt event loop unless running in interactive mode.
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):

View File

@@ -1,5 +1,7 @@
import math
import logging import logging
_log = logging.getLogger(__name__)
import math
from time import sleep from time import sleep
from PyQt5.QtCore import Qt, pyqtSignal from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtGui import QPainter, QBrush, QColor, QPainterPath, QPen, QDoubleValidator from PyQt5.QtGui import QPainter, QBrush, QColor, QPainterPath, QPen, QDoubleValidator
@@ -11,8 +13,6 @@ from epics.ca import pend_event
from app_utils import DeltatauMotorStatus, assert_motor_positions from app_utils import DeltatauMotorStatus, assert_motor_positions
Ui_MotorTweak, QWidget = loadUiType('epics_widgets/MotorTweak.ui') Ui_MotorTweak, QWidget = loadUiType('epics_widgets/MotorTweak.ui')
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
SPMG_STOP = 0 SPMG_STOP = 0
SPMG_PAUSE = 1 SPMG_PAUSE = 1
SPMG_MOVE = 2 SPMG_MOVE = 2
@@ -20,336 +20,340 @@ SPMG_GO = 3
class MotorTweak(QWidget, Ui_MotorTweak): class MotorTweak(QWidget, Ui_MotorTweak):
event_val = pyqtSignal(str, dict) event_val = pyqtSignal(str, dict)
event_readback = pyqtSignal(str, float, dict) event_readback = pyqtSignal(str, float, dict)
event_soft_limit = pyqtSignal(str, dict) event_soft_limit = pyqtSignal(str, dict)
event_high_hard_limit = pyqtSignal(str, dict) event_high_hard_limit = pyqtSignal(str, dict)
event_low_hard_limit = pyqtSignal(str, dict) event_low_hard_limit = pyqtSignal(str, dict)
event_axis_fault = pyqtSignal(str, dict) event_axis_fault = pyqtSignal(str, dict)
def __init__(self, parent=None): def __init__(self, parent=None):
super(MotorTweak, self).__init__(parent=parent) super(MotorTweak, self).__init__(parent=parent)
self.setupUi(self) self.setupUi(self)
self._wheel_tweaks = True self._wheel_tweaks = True
self._auto_labels = True self._auto_labels = True
self._locked = False self._locked = False
self._label_style = 'basic' self._label_style = 'basic'
self._templates_source = { self._templates_source = {
'basic': '<b>{short_name}</b> <font color="#080">{{rbv:.{precision}f}} {units}</font>', 'basic': '<b>{short_name}</b> <font color="#080">{{rbv:.{precision}f}} {units}</font>',
'small': '<small>{short_name} <font size="small" color="#080">{{rbv:.{precision}f}} {units}</font><small>', 'small': '<small>{short_name} <font size="small" color="#080">{{rbv:.{precision}f}} {units}</font><small>',
'2 lines': '<b>{short_name}</b><br><font size="small" color="#080">{{rbv:.{precision}f}} {units}</font>', '2 lines': '<b>{short_name}</b><br><font size="small" color="#080">{{rbv:.{precision}f}} {units}</font>',
'busy': '<b>{short_name}</b> <font color="#080">{{rbv:.{precision}f}} {units}</font>' 'busy': '<b>{short_name}</b> <font color="#080">{{rbv:.{precision}f}} {units}</font>'
} }
self._templates = {} self._templates = {}
def connect_motor(self, motor_base, short_name=None, **kwargs): def connect_motor(self, motor_base, short_name=None, **kwargs):
m = Motor(motor_base) m = Motor(motor_base)
m.get_position() m.get_position()
self._ignore_limits = m.HLM == m.LLM # if both high/low limits are equal they are meaningless self._ignore_limits = m.HLM == m.LLM # if both high/low limits are equal they are meaningless
self.motor = m self.motor = m
if not short_name: if not short_name:
short_name = m.description short_name = m.description
self.short_name = short_name self.short_name = short_name
self._pvname = motor_base self._pvname = motor_base
for attr in ['RTYP', 'JVEL', 'HLS', 'LLS', 'TWV', 'RBV', 'VAL', 'LVIO', 'HLM', 'LLM']: for attr in ['RTYP', 'JVEL', 'HLS', 'LLS', 'TWV', 'RBV', 'VAL', 'LVIO', 'HLM', 'LLM']:
# logger.debug('connecting to field {}'.format(attr)) # _log.debug('connecting to field {}'.format(attr))
m.PV(attr, connect=True) m.PV(attr, connect=True)
self.set_motor_validator() self.set_motor_validator()
self._drive_val.setText(m.get('VAL', as_string=True)) self._drive_val.setText(m.get('VAL', as_string=True))
self._drive_val.returnPressed.connect(self.move_motor_to_position) self._drive_val.returnPressed.connect(self.move_motor_to_position)
tweak_min = kwargs.get("tweak_min", 0.0001) tweak_min = kwargs.get("tweak_min", 0.0001)
tweak_max = kwargs.get("tweak_max", 10.0) tweak_max = kwargs.get("tweak_max", 10.0)
self._tweak_val.setText(m.get('TWV', as_string=True)) self._tweak_val.setText(m.get('TWV', as_string=True))
self._tweak_val.setValidator(QDoubleValidator(tweak_min, tweak_max, 4, self._tweak_val)) self._tweak_val.setValidator(QDoubleValidator(tweak_min, tweak_max, 4, self._tweak_val))
self._tweak_val.editingFinished.connect(lambda m=m: m.PV('TWV').put(self._tweak_val.text())) self._tweak_val.editingFinished.connect(lambda m=m: m.PV('TWV').put(self._tweak_val.text()))
self.jog_reverse.hide() self.jog_reverse.hide()
self.jog_forward.hide() self.jog_forward.hide()
# self.jog_forward.pressed.connect(lambda: self.jog('forward', True)) # self.jog_forward.pressed.connect(lambda: self.jog('forward', True))
# self.jog_reverse.pressed.connect(lambda: self.jog('reverse', True)) # self.jog_reverse.pressed.connect(lambda: self.jog('reverse', True))
# self.jog_forward.released.connect(lambda: self.jog('forward', False)) # self.jog_forward.released.connect(lambda: self.jog('forward', False))
# self.jog_reverse.released.connect(lambda: self.jog('reverse', False)) # self.jog_reverse.released.connect(lambda: self.jog('reverse', False))
self.tweak_forward.clicked.connect(lambda m: self.motor.tweak('forward')) self.tweak_forward.clicked.connect(lambda m: self.motor.tweak('forward'))
self.tweak_reverse.clicked.connect(lambda m: self.motor.tweak('reverse')) self.tweak_reverse.clicked.connect(lambda m: self.motor.tweak('reverse'))
self.bind_wheel() self.bind_wheel()
self.update_label_template() self.update_label_template()
m.add_callback('VAL', self.set_val) m.add_callback('VAL', self.set_val)
m.add_callback('RBV', self.update_label) m.add_callback('RBV', self.update_label)
m.set_callback('HLS', self.update_label) m.set_callback('HLS', self.update_label)
m.set_callback('LLS', self.update_label) m.set_callback('LLS', self.update_label)
m.set_callback('VAL', self.emit_signals, {'source_field': 'VAL'}) m.set_callback('VAL', self.emit_signals, {'source_field': 'VAL'})
m.set_callback('RBV', self.emit_signals, {'source_field': 'RBV'}) m.set_callback('RBV', self.emit_signals, {'source_field': 'RBV'})
m.set_callback('HLS', self.emit_signals, {'source_field': 'HLS'}) m.set_callback('HLS', self.emit_signals, {'source_field': 'HLS'})
m.set_callback('LLS', self.emit_signals, {'source_field': 'LLS'}) m.set_callback('LLS', self.emit_signals, {'source_field': 'LLS'})
m.set_callback('LVIO', self.emit_signals, {'source_field': 'LVIO'}) m.set_callback('LVIO', self.emit_signals, {'source_field': 'LVIO'})
m.set_callback('STAT', self.emit_signals, {'source_field': 'STAT'}) m.set_callback('STAT', self.emit_signals, {'source_field': 'STAT'})
m.set_callback('HLM', self.set_motor_validator) m.set_callback('HLM', self.set_motor_validator)
def set_val(self, **kw): def set_val(self, **kw):
v = kw['char_value'] v = kw['char_value']
logger.debug('updating VAL = {}'.format(v)) _log.debug('updating VAL = {}'.format(v))
self._drive_val.setText(v) self._drive_val.setText(v)
def set_motor_validator(self, **kwargs): def set_motor_validator(self, **kwargs):
m = self.motor m = self.motor
lineedit = self._drive_val lineedit = self._drive_val
min, max = m.PV('LLM').get(), m.PV('HLM').get() min, max = m.PV('LLM').get(), m.PV('HLM').get()
if min == max: if min == max:
min = -1e6 min = -1e6
max = 1e6 max = 1e6
lineedit.setValidator(QDoubleValidator(min, max, m.PV('PREC').get(), lineedit)) lineedit.setValidator(QDoubleValidator(min, max, m.PV('PREC').get(), lineedit))
def move_relative(self, dist): def move_relative(self, dist):
self.motor.move(dist, ignore_limits=True, relative=True) self.motor.move(dist, ignore_limits=True, relative=True)
def is_done(self): def is_done(self):
self.motor.refresh() self.motor.refresh()
return 1 == self.motor.done_moving return 1 == self.motor.done_moving
def get_position(self): def get_position(self):
return self.motor.get_position(readback=True) return self.motor.get_position(readback=True)
def is_homed(self): def is_homed(self):
self.motor.refresh() self.motor.refresh()
status = DeltatauMotorStatus(self.motor.motor_status) status = DeltatauMotorStatus(self.motor.motor_status)
return bool(DeltatauMotorStatus.MOTOR_HAS_BEEN_HOMED & status) return bool(DeltatauMotorStatus.MOTOR_HAS_BEEN_HOMED & status)
def move_motor_to_position(self, drive=None, wait=False, assert_position=False): def move_motor_to_position(self, drive=None, wait=False, assert_position=False):
if drive is None: if drive is None:
drive = float(self._drive_val.text()) drive = float(self._drive_val.text())
if assert_position: if assert_position:
wait=True wait=True
self.motor.move(drive, wait=wait, ignore_limits=True, relative=False) self.motor.move(drive, wait=wait, ignore_limits=True, relative=False)
if assert_position: if assert_position:
assert_motor_positions([(self.motor, drive, 0.1)], timeout=1) assert_motor_positions([(self.motor, drive, 0.1)], timeout=1)
def emit_signals(self, **kw): def emit_signals(self, **kw):
''' '''
:param kw: info about the channel { :param kw: info about the channel {
'access': 'read-only', 'access': 'read-only',
'char_value': '-0.105', 'char_value': '-0.105',
'chid': 36984304, 'chid': 36984304,
'count': 1, 'count': 1,
'enum_strs': None, 'enum_strs': None,
'ftype': 20, 'ftype': 20,
'host': 'SAR-CPPM-EXPMX1.psi.ch:5064', 'host': 'SAR-CPPM-EXPMX1.psi.ch:5064',
'lower_alarm_limit': None, 'lower_alarm_limit': None,
'lower_ctrl_limit': 0.0, 'lower_ctrl_limit': 0.0,
'lower_disp_limit': 0.0, 'lower_disp_limit': 0.0,
'lower_warning_limit': None, 'lower_warning_limit': None,
'motor_field': 'RBV', 'motor_field': 'RBV',
'nelm': 1, 'nelm': 1,
'precision': 3, 'precision': 3,
'pvname': 'SAR-EXPMX:MOT_ROT_Y.RBV', 'pvname': 'SAR-EXPMX:MOT_ROT_Y.RBV',
'read_access': True, 'read_access': True,
'severity': 0, 'severity': 0,
'source_field': 'RBV', 'source_field': 'RBV',
'status': 0, 'status': 0,
'timestamp': 1522314072.878331, 'timestamp': 1522314072.878331,
'type': 'time_double', 'type': 'time_double',
'typefull': 'time_double', 'typefull': 'time_double',
'units': b'deg', 'units': b'deg',
'upper_alarm_limit': None, 'upper_alarm_limit': None,
'upper_ctrl_limit': 0.0, 'upper_ctrl_limit': 0.0,
'upper_disp_limit': 0.0, 'upper_disp_limit': 0.0,
'upper_warning_limit': None, 'upper_warning_limit': None,
'value': -0.105, 'value': -0.105,
'write_access': False} 'write_access': False}
:return: :return:
''' '''
field = kw['motor_field'] field = kw['motor_field']
src = kw['source_field'] src = kw['source_field']
kw['alias'] = self.short_name kw['alias'] = self.short_name
if field != src: if field != src:
return return
if field == 'VAL': if field == 'VAL':
self.event_val.emit(self._pvname, kw) self.event_val.emit(self._pvname, kw)
elif field == 'RBV': elif field == 'RBV':
self.event_readback.emit(kw['alias'], kw['value'], kw) self.event_readback.emit(kw['alias'], kw['value'], kw)
elif field == 'LVIO': elif field == 'LVIO':
self.event_soft_limit.emit(self._pvname, kw) self.event_soft_limit.emit(self._pvname, kw)
elif field == 'HLS': elif field == 'HLS':
self.event_high_hard_limit.emit(self._pvname, kw) self.event_high_hard_limit.emit(self._pvname, kw)
self.event_axis_fault.emit(self._pvname, kw) self.event_axis_fault.emit(self._pvname, kw)
elif field == 'LVIO': elif field == 'LVIO':
self.event_low_hard_limit.emit(self._pvname, kw) self.event_low_hard_limit.emit(self._pvname, kw)
self.event_axis_fault.emit(self._pvname, kw) self.event_axis_fault.emit(self._pvname, kw)
elif field == 'STAT': elif field == 'STAT':
self.event_axis_fault.emit(self._pvname, kw) self.event_axis_fault.emit(self._pvname, kw)
def update_label(self, **kwargs): def update_label(self, **kwargs):
m = self.motor m = self.motor
self.label.setText(self._templates[self._label_style].format(rbv=m.readback)) self.label.setText(self._templates[self._label_style].format(rbv=m.readback))
self.jog_forward.setToolTip('jog forward at {:.3f} {}/s'.format(m.jog_speed, m.units)) self.jog_forward.setToolTip('jog forward at {:.3f} {}/s'.format(m.jog_speed, m.units))
self.jog_reverse.setToolTip('jog reverse at {:.3f} {}/s'.format(m.jog_speed, m.units)) self.jog_reverse.setToolTip('jog reverse at {:.3f} {}/s'.format(m.jog_speed, m.units))
self.tweak_forward.setToolTip('tweak forward by {:.3f} {}'.format(m.tweak_val, m.units)) self.tweak_forward.setToolTip('tweak forward by {:.3f} {}'.format(m.tweak_val, m.units))
self.tweak_reverse.setToolTip('tweak reverse by {:.3f} {}'.format(m.tweak_val, m.units)) self.tweak_reverse.setToolTip('tweak reverse by {:.3f} {}'.format(m.tweak_val, m.units))
def update_jog_speed(self, event): def update_jog_speed(self, event):
m = self.motor m = self.motor
speed = m.jog_speed speed = m.jog_speed
sign = math.copysign(1, event.angleDelta().y()) sign = math.copysign(1, event.angleDelta().y())
m.jog_speed = speed + (sign * 0.1 * speed) m.jog_speed = speed + (sign * 0.1 * speed)
self.jog_forward.setToolTip('jog forward at {:.3f} {}/s'.format(m.jog_speed, m.units)) self.jog_forward.setToolTip('jog forward at {:.3f} {}/s'.format(m.jog_speed, m.units))
self.jog_reverse.setToolTip('jog reverse at {:.3f} {}/s'.format(m.jog_speed, m.units)) self.jog_reverse.setToolTip('jog reverse at {:.3f} {}/s'.format(m.jog_speed, m.units))
def tweak_event(self, event): def tweak_event(self, event):
m = self.motor m = self.motor
sign = event.angleDelta().y() sign = event.angleDelta().y()
if sign < 0: if sign < 0:
m.tweak_reverse = 1 m.tweak_reverse = 1
else: else:
m.tweak_forward = 1 m.tweak_forward = 1
def bind_wheel(self): def bind_wheel(self):
self.tweak_forward.wheelEvent = self.tweak_event self.tweak_forward.wheelEvent = self.tweak_event
self.tweak_reverse.wheelEvent = self.tweak_event self.tweak_reverse.wheelEvent = self.tweak_event
def jog(self, direction, enable): def jog(self, direction, enable):
m = self.motor m = self.motor
if 'forw' in direction: if 'forw' in direction:
m.jog_forward = int(enable) m.jog_forward = int(enable)
else: else:
m.jog_reverse = int(enable) m.jog_reverse = int(enable)
def contextMenuEvent(self, event): def contextMenuEvent(self, event):
m = self.motor m = self.motor
menu = QMenu(self) menu = QMenu(self)
menu.setTitle(self.short_name) menu.setTitle(self.short_name)
lockmotor = QAction('lock motor', menu, checkable=True) lockmotor = QAction('lock motor', menu, checkable=True)
lockmotor.setChecked(self._locked) lockmotor.setChecked(self._locked)
menu.addAction(lockmotor) menu.addAction(lockmotor)
autolabelsAction = QAction('auto', menu, checkable=True) autolabelsAction = QAction('auto', menu, checkable=True)
autolabelsAction.setChecked(self._auto_labels) autolabelsAction.setChecked(self._auto_labels)
menu.addAction(autolabelsAction) menu.addAction(autolabelsAction)
wheel_tweaks = QAction('Mouse wheel tweaks motor', menu, checkable=True) wheel_tweaks = QAction('Mouse wheel tweaks motor', menu, checkable=True)
wheel_tweaks.setChecked(self._wheel_tweaks) wheel_tweaks.setChecked(self._wheel_tweaks)
menu.addAction(wheel_tweaks) menu.addAction(wheel_tweaks)
stopmotorAction = QAction('Stopped', menu, checkable=True) stopmotorAction = QAction('Stopped', menu, checkable=True)
stopmotorAction.setChecked(SPMG_STOP == m.stop_go) stopmotorAction.setChecked(SPMG_STOP == m.stop_go)
menu.addAction(stopmotorAction) menu.addAction(stopmotorAction)
changeprecAction = menu.addAction("Change Precision") changeprecAction = menu.addAction("Change Precision")
changejogspeedAction = menu.addAction("Jog speed {:.3f} {}/s".format(m.jog_speed, m.units)) changejogspeedAction = menu.addAction("Jog speed {:.3f} {}/s".format(m.jog_speed, m.units))
changetweakstepAction = menu.addAction("Tweak step {:.3f} {}".format(m.tweak_val, m.units)) changetweakstepAction = menu.addAction("Tweak step {:.3f} {}".format(m.tweak_val, m.units))
tozeroAction = menu.addAction("move to Zero") tozeroAction = menu.addAction("move to Zero")
action = menu.exec_(self.mapToGlobal(event.pos())) action = menu.exec_(self.mapToGlobal(event.pos()))
if action == lockmotor: if action == lockmotor:
self._locked = not self._locked self._locked = not self._locked
if self._locked: if self._locked:
self._controlbox.setDisabled(True) self._controlbox.setDisabled(True)
else: else:
self._controlbox.setDisabled(False) self._controlbox.setDisabled(False)
elif action == changeprecAction: elif action == changeprecAction:
name = m.NAME name = m.NAME
prec = m.PREC prec = m.PREC
msg = 'Precision for motor {}'.format(name) msg = 'Precision for motor {}'.format(name)
logger.debug('prec before %d', prec) _log.debug('prec before %d', prec)
prec, ok = QInputDialog.getInt(self, msg, msg, prec, 0, 10) prec, ok = QInputDialog.getInt(self, msg, msg, prec, 0, 10)
logger.debug('prec after (%d) %d', ok, prec) _log.debug('prec after (%d) %d', ok, prec)
if ok: if ok:
self.motor.put('PREC', prec, wait=True) self.motor.put('PREC', prec, wait=True)
elif action == changejogspeedAction: elif action == changejogspeedAction:
name = m.NAME name = m.NAME
speed = m.jog_speed speed = m.jog_speed
msg = 'Jog speed for motor {}'.format(name) msg = 'Jog speed for motor {}'.format(name)
speed, ok = QInputDialog.getDouble(self, msg, msg, speed, 0.01, m.slew_speed, m.precision) speed, ok = QInputDialog.getDouble(self, msg, msg, speed, 0.01, m.slew_speed, m.precision)
if ok: if ok:
self.motor.put('JVEL', speed, wait=True) self.motor.put('JVEL', speed, wait=True)
elif action == changetweakstepAction: elif action == changetweakstepAction:
name = m.NAME name = m.NAME
tv = m.tweak_val tv = m.tweak_val
msg = 'Tweak step for motor {} at {} {}'.format(name, tv, m.units) msg = 'Tweak step for motor {} at {} {}'.format(name, tv, m.units)
tv, ok = QInputDialog.getDouble(self, msg, msg, tv, pow(10, -m.precision), m.slew_speed, m.precision) tv, ok = QInputDialog.getDouble(self, msg, msg, tv, pow(10, -m.precision), m.slew_speed, m.precision)
if ok: if ok:
self.motor.put('TWV', tv, wait=True) self.motor.put('TWV', tv, wait=True)
elif action == tozeroAction: elif action == tozeroAction:
m.move(0.0, ignore_limits=True) m.move(0.0, ignore_limits=True)
elif action == stopmotorAction: elif action == stopmotorAction:
self.motor.stop_go = SPMG_STOP if m.stop_go == SPMG_GO else SPMG_GO self.motor.stop_go = SPMG_STOP if m.stop_go == SPMG_GO else SPMG_GO
elif action == autolabelsAction: elif action == autolabelsAction:
self._auto_labels = not self._auto_labels self._auto_labels = not self._auto_labels
elif action == wheel_tweaks: elif action == wheel_tweaks:
self._wheel_tweaks = not self._wheel_tweaks self._wheel_tweaks = not self._wheel_tweaks
self.bind_wheel() self.bind_wheel()
self.update_label_template() self.update_label_template()
def resizeEvent(self, event): def resizeEvent(self, event):
return # FIXME disable for the time being return # FIXME disable for the time being
if not self._auto_labels: if not self._auto_labels:
return return
w, h = event.size().width(), event.size().height() w, h = event.size().width(), event.size().height()
if w < 400: if w < 400:
self.jog_reverse.hide() self.jog_reverse.hide()
self.jog_forward.hide() self.jog_forward.hide()
else: else:
self.jog_reverse.show() self.jog_reverse.show()
self.jog_forward.show() self.jog_forward.show()
self.update_label() self.update_label()
def update_label_template(self): def update_label_template(self):
m = self.motor m = self.motor
source = self._templates_source source = self._templates_source
target = self._templates target = self._templates
for k in source: for k in source:
target[k] = source[k].format( target[k] = source[k].format(
short_name=self.short_name, short_name=self.short_name,
precision=m.PREC, precision=m.PREC,
units=m.units) units=m.units)
self.label.setText(target[self._label_style].format(rbv=m.readback)) self.label.setText(target[self._label_style].format(rbv=m.readback))
def paintEvent(self, e): def paintEvent(self, e):
qp = QPainter() qp = QPainter()
qp.begin(self) qp.begin(self)
qp.setRenderHint(QPainter.Antialiasing) qp.setRenderHint(QPainter.Antialiasing)
self._draw_limits(qp) self._draw_limits(qp)
qp.end() qp.end()
def _draw_limits(self, qp): def _draw_limits(self, qp):
m = self.motor try:
width, height = self.size().width(), self.size().height() m = self.motor
pad = 5 except AttributeError:
rounding = 2 _log.warning('Motor not connected')
size = 10 return
if m.HLS: width, height = self.size().width(), self.size().height()
x, y, w, h = width - size, pad, size, height - 2 * pad pad = 5
elif m.LLS: rounding = 2
x, y, w, h = 0, pad, size, height - 2 * pad size = 10
else: if m.HLS:
return x, y, w, h = width - size, pad, size, height - 2 * pad
color = QColor('indianred') elif m.LLS:
qp.setBrush(QBrush(color, Qt.SolidPattern)) x, y, w, h = 0, pad, size, height - 2 * pad
qp.setPen(QPen(color)) else:
path = QPainterPath() return
path.setFillRule(Qt.WindingFill) color = QColor('indianred')
path.addRoundedRect(x, y, w, h, rounding, rounding) qp.setBrush(QBrush(color, Qt.SolidPattern))
qp.drawPath(path) qp.setPen(QPen(color))
path = QPainterPath()
path.setFillRule(Qt.WindingFill)
path.addRoundedRect(x, y, w, h, rounding, rounding)
qp.drawPath(path)

5960
swissmx.py

File diff suppressed because it is too large Load Diff