switch to motorRecord Smaract
This commit is contained in:
29
Readme.md
29
Readme.md
@@ -291,17 +291,44 @@ bt
|
||||
Localize mionitors and callbacks:
|
||||
```
|
||||
|
||||
grep -n 'def cb_update_img(self):' *.py
|
||||
grep -Rn 'add_callback' *.py
|
||||
|
||||
|
||||
swissmx.py:698: self.sigNewCamImg.connect(self.cb_update_img)
|
||||
swissmx.py:745: def cb_update_img(self):
|
||||
epics_widgets/MotorTweak.py:88: m.set_callback('RBV', self.emit_signals, {'source_field': 'RBV'})
|
||||
epics_widgets/SmaractMotorTweak.py:100: self._pv_readback.add_callback(self.update_label)
|
||||
swissmx.py:717: cam.run(self.cb_new_frame_pv)
|
||||
swissmx.py:743: self.sigNewCamImg.emit()
|
||||
swissmx.py:745: def cb_update_img(self):
|
||||
swissmx.py:698: self.sigNewCamImg.connect(self.cb_update_img)
|
||||
camera.py:159: self._pv['pic'] = epics.PV(self._prefix + "FPICTURE", auto_monitor=True, callback=cb)
|
||||
|
||||
|
||||
-> try to turn of the monitors during ascuisition:
|
||||
zamofing_t@ganymede:~/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX$
|
||||
grep -r set_callback *
|
||||
|
||||
https://pyepics.github.io/pyepics/pv.html#automatic-monitoring-of-a-pv
|
||||
|
||||
|
||||
Try with:
|
||||
pv.clear_auto_monitor() -> pv.reconnect()
|
||||
|
||||
pv.remove_callback(index=None) -> pv.add_callback(callback=None[, index=None [, with_ctrlvars=True[, **kw]])
|
||||
pv.set_callback
|
||||
|
||||
FixTargetFrame -> paint -> _log.debug()
|
||||
|
||||
|
||||
Turn off jungfrau.
|
||||
no motion -> wait 1h
|
||||
constant up-dow motion code -> wait 1h
|
||||
|
||||
|
||||
```
|
||||
|
||||
try: #pv-monitor-func
|
||||
|
||||
----------------------------------- SCRATCH -----------------------------------
|
||||
|
||||
|
||||
@@ -1,341 +0,0 @@
|
||||
import math
|
||||
import logging
|
||||
from time import sleep
|
||||
from PyQt5.QtCore import Qt, pyqtSignal
|
||||
from PyQt5.QtGui import QPainter, QBrush, QColor, QPainterPath, QPen, QDoubleValidator
|
||||
from PyQt5.QtWidgets import QMenu, QInputDialog, QAction
|
||||
from PyQt5.uic import loadUiType
|
||||
from epics import PV
|
||||
from epics.ca import pend_event
|
||||
from app_utils import assert_tweaker_positions
|
||||
|
||||
Ui_MotorTweak, QWidget = loadUiType('epics_widgets/MotorTweak.ui')
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
SPMG_STOP = 0
|
||||
SPMG_PAUSE = 1
|
||||
SPMG_MOVE = 2
|
||||
SPMG_GO = 3
|
||||
|
||||
|
||||
|
||||
class SmaractMotorTweak(QWidget, Ui_MotorTweak):
|
||||
event_val = pyqtSignal(str, dict)
|
||||
event_rbv = pyqtSignal(str, dict)
|
||||
event_soft_limit = pyqtSignal(str, dict)
|
||||
event_high_hard_limit = pyqtSignal(str, dict)
|
||||
event_low_hard_limit = pyqtSignal(str, dict)
|
||||
event_axis_fault = pyqtSignal(str, dict)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(SmaractMotorTweak, self).__init__(parent=parent)
|
||||
self.setupUi(self)
|
||||
self._wheel_tweaks = True
|
||||
self._auto_labels = True
|
||||
self._locked = False
|
||||
self._label_style = 'basic'
|
||||
self._templates_source = {
|
||||
'basic': '<b>{label}</b> <font color="#080">{{rbv:.{precision}f}} {units}</font>',
|
||||
'small': '<small>{label} <font size="small" color="#080">{{rbv:.{precision}f}} {units}</font><small>',
|
||||
'2 lines': '<b>{label}</b><br><font size="small" color="#080">{{rbv:.{precision}f}} {units}</font>',
|
||||
'busy': '<b>{label}</b> <font color="#080">{{rbv:.{precision}f}} {units}</font>'
|
||||
}
|
||||
self._templates = {}
|
||||
|
||||
|
||||
def connect_motor(self, rec_name, **kwargs):
|
||||
# TODO: DO NOT USE so many PVs, but reduce to the only really needed PV: s.a. class QopticZoom(object)
|
||||
label=kwargs['label']
|
||||
self._label=label
|
||||
self._pvname = rec_name+':DRIVE'
|
||||
self._pv_name = PV(rec_name+':NAME')
|
||||
self._pv_drive = PV(rec_name+':DRIVE')
|
||||
self._pv_readback = PV(rec_name+':MOTRBV')
|
||||
self._pv_tweak_r = PV(rec_name+':TWR.PROC')
|
||||
self._pv_tweak_f = PV(rec_name+':TWF.PROC')
|
||||
self._pv_tweak_val = PV(rec_name+':TWV')
|
||||
self._pv_status = PV(rec_name + ':STATUS')
|
||||
self._pv_home_f = PV(rec_name + ':FRM_FORW.PROC')
|
||||
self._pv_home_b = PV(rec_name + ':FRM_BACK.PROC')
|
||||
self._pv_is_homed = PV(rec_name + ':GET_HOMED')
|
||||
self._pv_llm = PV(rec_name + ':LLM')
|
||||
self._pv_hlm = PV(rec_name + ':HLM')
|
||||
|
||||
self.label.setToolTip('{} => {}'.format(rec_name, self._pv_name.get()))
|
||||
|
||||
try:
|
||||
self._prec = kwargs['prec']
|
||||
except:
|
||||
self._prec = 5
|
||||
|
||||
try:
|
||||
self._units = kwargs['units']
|
||||
except:
|
||||
self._units = 'mm'
|
||||
|
||||
self._ignore_limits = self._pv_llm.get() == self._pv_hlm.get() # if both high/low limits are equal they are meaningless
|
||||
|
||||
self.set_motor_validator()
|
||||
self._drive_val.setText(self._pv_drive.get(as_string=True))
|
||||
self._drive_val.returnPressed.connect(self.move_abs)
|
||||
|
||||
self.jog_forward.hide()
|
||||
self.jog_reverse.hide()
|
||||
|
||||
tweak_min = kwargs.get("tweak_min", 0.0001)
|
||||
tweak_max = kwargs.get("tweak_max", 20.0)
|
||||
self._tweak_val.setText(self._pv_tweak_val.get(as_string=True))
|
||||
self._tweak_val.setValidator(QDoubleValidator(tweak_min, tweak_max, 4, self._tweak_val))
|
||||
self._tweak_val.editingFinished.connect(lambda m=self._pv_tweak_val: m.put(self._tweak_val.text()))
|
||||
|
||||
self.tweak_forward.clicked.connect(lambda m: self._pv_tweak_f.put(1))
|
||||
self.tweak_reverse.clicked.connect(lambda m: self._pv_tweak_r.put(1))
|
||||
|
||||
|
||||
self.bind_wheel()
|
||||
|
||||
self.update_label_template()
|
||||
|
||||
self._pv_readback.add_callback(self.update_label)
|
||||
self._pv_status.add_callback(self.set_val_on_status_change)
|
||||
|
||||
# self._pv_drive.add_callback(self.set_val)
|
||||
self._pv_drive.add_callback(self.emit_signals, source_field='VAL')
|
||||
self._pv_readback.add_callback(self.emit_signals, source_field='RBV')
|
||||
self._pv_hlm.add_callback(self.emit_signals, source_field='HLS')
|
||||
self._pv_llm.add_callback(self.emit_signals, source_field='LLS')
|
||||
|
||||
# m.set_callback('HLM', self.set_motor_validator)
|
||||
|
||||
|
||||
def set_motor_validator(self, **kwargs):
|
||||
lineedit = self._drive_val
|
||||
min, max = self._pv_llm.get(), self._pv_hlm.get()
|
||||
if min == max:
|
||||
min = -1e6
|
||||
max = 1e6
|
||||
lineedit.setValidator(QDoubleValidator(min, max, 5, lineedit))
|
||||
|
||||
|
||||
def move_rel(self, dist, delay=None):
|
||||
cur = self._pv_readback.get()
|
||||
target = cur + dist
|
||||
if delay:
|
||||
sleep(delay)
|
||||
self._pv_drive.put(target)
|
||||
|
||||
def get_rbv(self):
|
||||
return self._pv_readback.get(use_monitor=False)
|
||||
|
||||
def get_val(self):
|
||||
# return self._motor.get('VAL')
|
||||
try:
|
||||
v=self._val
|
||||
except AttributeError:
|
||||
self._val=v=self._pv_drive.get()
|
||||
return v
|
||||
|
||||
def is_homed(self):
|
||||
pend_event()
|
||||
return 1 == self._pv_is_homed.get(use_monitor=False)
|
||||
|
||||
def get_status(self, as_string=False):
|
||||
"""
|
||||
States:
|
||||
0 Stopped
|
||||
1 Stepping
|
||||
2 Scanning
|
||||
3 Holding
|
||||
4 Targeting
|
||||
5 Move Delay
|
||||
6 Calibrating
|
||||
7 Finding Ref
|
||||
8 Locked
|
||||
:return:
|
||||
"""
|
||||
return self._pv_status.get(as_string=as_string, use_monitor=False)
|
||||
|
||||
def wait(self):
|
||||
pend_event(.01)
|
||||
while not self.is_done():
|
||||
pend_event(0.2)
|
||||
|
||||
def is_done(self):
|
||||
return self._pv_status.get() in (0, 3)
|
||||
|
||||
def move_abs(self, drive=None, wait=False, assert_position=False):
|
||||
if assert_position:
|
||||
wait=True
|
||||
if drive is None:
|
||||
logger.debug('{} abs target from widget'.format(self._label))
|
||||
drive = float(self._drive_val.text())
|
||||
logger.debug('{} abs move => {}'.format(self._label, drive))
|
||||
self._pv_drive.put(drive)
|
||||
if wait:
|
||||
self.wait()
|
||||
if assert_position:
|
||||
assert_tweaker_positions([(self, drive, 0.05)], timeout=10.)
|
||||
|
||||
def emit_signals(self, **kw):
|
||||
field = kw['source_field']
|
||||
if field == 'VAL':
|
||||
self.event_val.emit(self._pvname, kw)
|
||||
elif field == 'RBV':
|
||||
self.event_rbv.emit(self._pvname, kw)
|
||||
elif field == 'LVIO':
|
||||
self.event_soft_limit.emit(self._pvname, kw)
|
||||
elif field == 'HLS':
|
||||
self.event_high_hard_limit.emit(self._pvname, kw)
|
||||
self.event_axis_fault.emit(self._pvname, kw)
|
||||
elif field == 'LVIO':
|
||||
self.event_low_hard_limit.emit(self._pvname, kw)
|
||||
self.event_axis_fault.emit(self._pvname, kw)
|
||||
elif field == 'STAT':
|
||||
self.event_axis_fault.emit(self._pvname, kw)
|
||||
|
||||
|
||||
def set_val_on_status_change(self, **kw):
|
||||
'''
|
||||
0 Stopped
|
||||
1 Stepping
|
||||
2 Scanning
|
||||
3 Holding
|
||||
4 Targeting
|
||||
5 Move Delay
|
||||
6 Calibrating
|
||||
7 Finding Ref
|
||||
8 Locked
|
||||
:param kw:
|
||||
:return:
|
||||
'''
|
||||
status = kw['char_value']
|
||||
if status in ('Holding', 'Stopped'):
|
||||
v = self._pv_readback.get(as_string=True)
|
||||
logger.debug('updating VAL on status change to holding/stopped = {}'.format(v))
|
||||
self._drive_val.setText(v)
|
||||
|
||||
def set_val(self, **kw):
|
||||
v = kw['char_value']
|
||||
logger.debug('updating VAL = {}'.format(v))
|
||||
self._drive_val.setText(v)
|
||||
|
||||
def update_label(self, **kwargs):
|
||||
self.label.setText(self._templates[self._label_style].format(rbv=self._pv_readback.get()))
|
||||
self.tweak_forward.setToolTip('tweak forward by {:.3f} {}'.format(self._pv_tweak_val.get(), self._units))
|
||||
self.tweak_reverse.setToolTip('tweak reverse by {:.3f} {}'.format(self._pv_tweak_val.get(), self._units))
|
||||
|
||||
def tweak_event(self, event):
|
||||
sign = event.angleDelta().y()
|
||||
if sign < 0:
|
||||
self._pv_tweak_r.put(1)
|
||||
else:
|
||||
self._pv_tweak_f.put(1)
|
||||
|
||||
def bind_wheel(self):
|
||||
self.tweak_forward.wheelEvent = self.tweak_event
|
||||
self.tweak_reverse.wheelEvent = self.tweak_event
|
||||
|
||||
def contextMenuEvent(self, event):
|
||||
name = self._pv_name.get()
|
||||
unit = self._units
|
||||
prec = self._prec
|
||||
|
||||
menu = QMenu(self)
|
||||
menu.setTitle(self._label)
|
||||
|
||||
lockmotor = QAction('lock motor', menu, checkable=True)
|
||||
lockmotor.setChecked(self._locked)
|
||||
menu.addAction(lockmotor)
|
||||
|
||||
autolabelsAction = QAction('auto', menu, checkable=True)
|
||||
autolabelsAction.setChecked(self._auto_labels)
|
||||
menu.addAction(autolabelsAction)
|
||||
|
||||
wheel_tweaks = QAction('Mouse wheel tweaks motor', menu, checkable=True)
|
||||
wheel_tweaks.setChecked(self._wheel_tweaks)
|
||||
menu.addAction(wheel_tweaks)
|
||||
|
||||
# stopmotorAction = QAction('Stopped', menu, checkable=True)
|
||||
# stopmotorAction.setChecked(SPMG_STOP == m.stop_go)
|
||||
# menu.addAction(stopmotorAction)
|
||||
changeprecAction = menu.addAction("Change Precision")
|
||||
changetweakstepAction = menu.addAction("Tweak step {:.3f} {}".format(self._pv_tweak_val.get(), unit))
|
||||
|
||||
tozeroAction = menu.addAction("move to Zero")
|
||||
|
||||
|
||||
action = menu.exec_(self.mapToGlobal(event.pos()))
|
||||
if action == lockmotor:
|
||||
self._locked = not self._locked
|
||||
if self._locked:
|
||||
self._controlbox.setDisabled(True)
|
||||
else:
|
||||
self._controlbox.setDisabled(False)
|
||||
elif action == changeprecAction:
|
||||
msg = 'Precision for motor {}'.format(name)
|
||||
logger.debug('prec before %d', prec)
|
||||
prec, ok = QInputDialog.getInt(self, msg, msg, prec, 0, 10)
|
||||
logger.debug('prec after (%d) %d', ok, prec)
|
||||
if ok:
|
||||
self._prec = prec
|
||||
|
||||
elif action == changetweakstepAction:
|
||||
tv = self._pv_tweak_val.get()
|
||||
msg = 'Tweak step for motor {} at {} {}'.format(name, tv, unit)
|
||||
tv, ok = QInputDialog.getDouble(self, msg, msg, tv, pow(10, -prec), 10.0, prec)
|
||||
if ok:
|
||||
self._pv_tweak_val.put(tv)
|
||||
|
||||
elif action == tozeroAction:
|
||||
self._pv_drive.put(0.0)
|
||||
|
||||
elif action == autolabelsAction:
|
||||
self._auto_labels = not self._auto_labels
|
||||
|
||||
elif action == wheel_tweaks:
|
||||
self._wheel_tweaks = not self._wheel_tweaks
|
||||
self.bind_wheel()
|
||||
|
||||
self.update_label_template()
|
||||
|
||||
def resizeEvent(self, event):
|
||||
pass
|
||||
|
||||
def update_label_template(self):
|
||||
source = self._templates_source
|
||||
target = self._templates
|
||||
|
||||
for k in source:
|
||||
target[k] = source[k].format(
|
||||
label=self._label,
|
||||
precision=self._prec,
|
||||
units=self._units)
|
||||
self.label.setText(target[self._label_style].format(rbv=self._pv_readback.get()))
|
||||
|
||||
def paintEvent(self, e):
|
||||
qp = QPainter()
|
||||
qp.begin(self)
|
||||
qp.setRenderHint(QPainter.Antialiasing)
|
||||
self._draw_limits(qp)
|
||||
qp.end()
|
||||
|
||||
def _draw_limits(self, qp):
|
||||
pass
|
||||
# width, height = self.size().width(), self.size().height()
|
||||
# pad = 5
|
||||
# rounding = 2
|
||||
# size = 10
|
||||
# if m.HLS:
|
||||
# x, y, w, h = width - size, pad, size, height - 2 * pad
|
||||
# elif m.LLS:
|
||||
# x, y, w, h = 0, pad, size, height - 2 * pad
|
||||
# else:
|
||||
# return
|
||||
# color = QColor('indianred')
|
||||
# qp.setBrush(QBrush(color, Qt.SolidPattern))
|
||||
# qp.setPen(QPen(color))
|
||||
# path = QPainterPath()
|
||||
# path.setFillRule(Qt.WindingFill)
|
||||
# path.addRoundedRect(x, y, w, h, rounding, rounding)
|
||||
# qp.drawPath(path)
|
||||
27
swissmx.py
27
swissmx.py
@@ -143,7 +143,6 @@ from PyQt5.uic import loadUiType
|
||||
ts.log('Import part 4/8:')
|
||||
import pyqtUsrObj as UsrGO
|
||||
from epics_widgets.MotorTweak import MotorTweak
|
||||
from epics_widgets.SmaractMotorTweak import SmaractMotorTweak
|
||||
from epics_widgets.SimMotorTweak import SimMotorTweak
|
||||
ts.log('Import part 5/8:')
|
||||
import numpy as np
|
||||
@@ -906,7 +905,7 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
|
||||
if sim&0x20:
|
||||
m_tw=SimMotorTweak()
|
||||
else:
|
||||
m_tw=SmaractMotorTweak()
|
||||
m_tw=MotorTweak()
|
||||
m_tw.connect_motor(rec, **kwargs)
|
||||
self.tweakers[kwargs['alias']] = m_tw
|
||||
return m_tw
|
||||
@@ -1019,6 +1018,8 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
|
||||
|
||||
def build_group_collimator(self, toolbox):
|
||||
pfx=QApplication.instance()._cfg.value(AppCfg.GBL_DEV_PREFIX)[1]
|
||||
_log.warning('TODO: cleanup PV names!!!')
|
||||
pfx='SARES30-SMX'
|
||||
c=QWidget()
|
||||
c.setLayout(QGridLayout())
|
||||
f=lambda v: lambda x:self.move_collimator(v) #needs nested lambda to work ...
|
||||
@@ -1027,8 +1028,8 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
|
||||
but.clicked.connect(f(v))
|
||||
c.layout().addWidget(but, 0, i)
|
||||
widgets = [
|
||||
self.get_tweaker(f"{pfx}1", alias="colli_x", label="colli X", mtype=1,),
|
||||
self.get_tweaker(f"{pfx}2", alias="colli_y", label="colli Y", mtype=1,),
|
||||
self.get_tweaker(f"{pfx}:MCS1", alias="colli_x", label="colli X", mtype=1,),
|
||||
self.get_tweaker(f"{pfx}:MCS2", alias="colli_y", label="colli Y", mtype=1,),
|
||||
c,
|
||||
]
|
||||
qutilities.add_item_to_toolbox(toolbox,"Collimator",widget_list=widgets)
|
||||
@@ -1036,12 +1037,14 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
|
||||
|
||||
def build_group_posttube(self, toolbox):
|
||||
pfx=QApplication.instance()._cfg.value(AppCfg.GBL_DEV_PREFIX)[1]
|
||||
_log.warning('TODO: cleanup PV names!!!')
|
||||
pfx='SARES30-SMX'
|
||||
widgets = [
|
||||
self.get_tweaker(f"{pfx}4", alias="tube_usx", label="upstream X", mtype=1,),
|
||||
self.get_tweaker(f"{pfx}6", alias="tube_usy", label="upstream Y", mtype=1,),
|
||||
self.get_tweaker(f"{pfx}5", alias="tube_dsx", label="downstream X", mtype=1,),
|
||||
self.get_tweaker(f"{pfx}7", alias="tube_dsy", label="downstream Y", mtype=1,),
|
||||
self.get_tweaker(f"{pfx}8", alias="tube_z", label="tube Z", mtype=1),
|
||||
self.get_tweaker(f"{pfx}:MCS4", alias="tube_usx", label="upstream X", mtype=1,),
|
||||
self.get_tweaker(f"{pfx}:MCS6", alias="tube_usy", label="upstream Y", mtype=1,),
|
||||
self.get_tweaker(f"{pfx}:MCS5", alias="tube_dsx", label="downstream X", mtype=1,),
|
||||
self.get_tweaker(f"{pfx}:MCS7", alias="tube_dsy", label="downstream Y", mtype=1,),
|
||||
self.get_tweaker(f"{pfx}:MCS8", alias="tube_z", label="tube Z", mtype=1),
|
||||
]
|
||||
c = QWidget()
|
||||
c.setLayout(QGridLayout())
|
||||
@@ -1067,9 +1070,11 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
|
||||
|
||||
def build_group_xeye(self, toolbox):
|
||||
pfx=QApplication.instance()._cfg.value(AppCfg.GBL_DEV_PREFIX)[1]
|
||||
_log.warning('TODO: cleanup PV names!!!')
|
||||
pfx='SARES30-SMX'
|
||||
widgets=[
|
||||
self.get_tweaker(f"{pfx}14", alias="xeye_x", label="X", mtype=1),
|
||||
self.get_tweaker(f"{pfx}15", alias="xeye_y", label="Y", mtype=1),
|
||||
self.get_tweaker(f"{pfx}:MCS14", alias="xeye_x", label="X", mtype=1),
|
||||
self.get_tweaker(f"{pfx}:MCS15", alias="xeye_y", label="Y", mtype=1),
|
||||
]
|
||||
qutilities.add_item_to_toolbox(toolbox,"X-Ray Eye",widget_list=widgets)
|
||||
WndSwissMx.pv_monitor_add(widgets)
|
||||
|
||||
Reference in New Issue
Block a user