diff --git a/ModuleFixTarget.py b/ModuleFixTarget.py index 961827f..2e01ba2 100644 --- a/ModuleFixTarget.py +++ b/ModuleFixTarget.py @@ -220,7 +220,7 @@ class WndFixTarget(QWidget): # def contextMenuEvent(self, event) is called... but this requests to overload the function # and to build an own context menu # much simpler is to use Qt.ActionsContextMenu - # and to handle the context in the parent plass + # and to handle the context in the parent class tree.setContextMenuPolicy(Qt.ActionsContextMenu) diff --git a/Readme.md b/Readme.md index 047469a..13b511a 100644 --- a/Readme.md +++ b/Readme.md @@ -325,6 +325,12 @@ avoid constand RBV event: caput SAR-ESPMX:MOT_FX 0.001 caput SAR-ESPMX:MOT_FY 0.001 ---------- TODO ---------- + +caput SAR-ESPMX:MOT_FX.VELO 16 +caput SAR-ESPMX:MOT_FY.VELO 16 + + + -faster autofocus: - move at constant speed and acquire as fast as possible instead stop and go motion @@ -335,8 +341,18 @@ caput SAR-ESPMX:MOT_FY 0.001 - automation: towards automatic alignement - - + if idx==0: + #go=UsrGO.Fiducial(bm_pos+bm_sz/2-(20, 20), (40, 40),(fx,fy,bz)) + l=.120 + go=UsrGO.Fiducial((fx-l/2,fy-l/2), (l, l),bz) + go.sigRegionChangeFinished.connect(self.cb_fiducial_update_z) + grp=self._goTracked + grp.addItem(go) + mft._tree.setData(grp.childItems()) + + + ="geometry/find_fiducial" + GEO_FND_FID,GEO_AUTOFOC="geometry/autofocus" diff --git a/app_config.py b/app_config.py index 5d40194..2c7d36d 100644 --- a/app_config.py +++ b/app_config.py @@ -92,7 +92,7 @@ class AppCfg(QSettings): if AppCfg.GEO_BEAM_SZ not in keys: dflt.append((AppCfg.GEO_BEAM_SZ, [30.2, 25.6])) if AppCfg.GEO_BEAM_POS not in keys: - dflt.append((AppCfg.GEO_BEAM_POS, [23.4, -54.2])) + dflt.append((AppCfg.GEO_BEAM_POS, [23.4, 54.2])) if AppCfg.GEO_FND_FID not in keys: dflt.append((AppCfg.GEO_FND_FID, {'sz':101, 'brd':8, 'pitch':120, 'mode':0})) if AppCfg.GEO_AUTOFOC not in keys: @@ -307,12 +307,13 @@ verbose bits: {'name':'set_out', 'title':'use current position as "out"', 'type':'action'}, ]}, {'name': AppCfg.DFT_POS_BKLGT, 'title':'Back Light reference positions', 'type':'group','expanded':False, 'children':[ - {'name':'pos_in', 'title':'In position', 'value':dft_pos_bklgt.get('pos_in',0), 'type':'float', 'limits':(-30000, 10 ),'step':10, 'decimals':5, 'suffix':'ustep'}, - {'name':'pos_out','title':'Out position', 'value':dft_pos_bklgt.get('pos_out',0), 'type':'float', 'limits':(-1000, 10 ),'step':10, 'decimals':5, 'suffix':'ustep'}, + {'name':'pos_in', 'title':'In position', 'value':dft_pos_bklgt.get('pos_in',0), 'type':'float', 'limits':(-30000, 10 ),'step':10, 'decimals':5, 'suffix':'ustep'}, + {'name':'pos_out', 'title':'Out position', 'value':dft_pos_bklgt.get('pos_out',0), 'type':'float', 'limits':(-30000, 10 ),'step':10, 'decimals':5, 'suffix':'ustep'}, + {'name':'pos_diode','title':'Diode pos', 'value':dft_pos_bklgt.get('pos_diode', 0), 'type':'float','limits':(-30000, 10), 'step':10, 'decimals':5, 'suffix':'ustep'}, ]}, {'name': AppCfg.DFT_POS_DET, 'title':'detector reference positions', 'type':'group','expanded':False, 'children':[ - {'name':'pos_in', 'title':'In position', 'value':dft_pos_det.get('pos_in',0), 'type':'float', 'limits':(-20, 20 ),'step':0.1, 'decimals':5, 'suffix':' mm'}, - {'name':'pos_out','title':'Out position', 'value':dft_pos_det.get('pos_out',0), 'type':'float', 'limits':(-20, 20 ),'step':0.1, 'decimals':5, 'suffix':' mm'}, + {'name':'pos_in', 'title':'In position', 'value':dft_pos_det.get('pos_in',0), 'type':'float', 'limits':(-200, 200),'step':0.1, 'decimals':5, 'suffix':' mm'}, + {'name':'pos_out','title':'Out position', 'value':dft_pos_det.get('pos_out',0), 'type':'float', 'limits':(-200, 200),'step':0.1, 'decimals':5, 'suffix':' mm'}, {'name':'set_in', 'title':'use current position as "in"', 'type':'action'}, {'name':'set_out', 'title':'use current position as "out"', 'type':'action'}, ]}, diff --git a/app_utils.py b/app_utils.py index 3ec2458..a4ab985 100644 --- a/app_utils.py +++ b/app_utils.py @@ -1,104 +1,85 @@ import time import logging -import enum -logger = logging.getLogger(__name__) -logger.setLevel(logging.DEBUG) +_log=logging.getLogger(__name__) from epics.ca import pend_event -class DeltatauMotorStatus(enum.Flag): - """names are taken from motorx_all.ui""" - DIRECTION = enum.auto() - MOTION_IS_COMPLETE = enum.auto() - PLUS_LIMIT_SWITCH = enum.auto() - HOME_SWITCH = enum.auto() - UNUSED_1 = enum.auto() - CLOSED_LOOP_POSITION = enum.auto() - SLIP_STALL_DETECTED = enum.auto() - AT_HOME_POSITION = enum.auto() - ENCODER_IS_PRESENT = enum.auto() - PROBLEM = enum.auto() - MOVING = enum.auto() - GAIN_SUPPORT = enum.auto() - COMMUNICATION_ERROR = enum.auto() - MINUS_LIMIT_SWITCH = enum.auto() - MOTOR_HAS_BEEN_HOMED = enum.auto() - - AT_SWITCH = MINUS_LIMIT_SWITCH | PLUS_LIMIT_SWITCH class PositionsNotReached(Exception): - pass + pass + def assert_tweaker_positions(targets, timeout=60.0): - """ - wait for each of the given motors to have specified positions within tolerance - :param targets: [ - (motor, target_position, tolerance), - ... - ] - :return: None - :raises: PositionsNotReached - """ - num_motors = len(targets) + """ + wait for each of the given motors to have specified positions within tolerance + :param targets: [ + (motor, target_position, tolerance), + ... + ] + :return: None + :raises: PositionsNotReached + """ + num_motors=len(targets) - timeisup = timeout + time.time() + timeisup=timeout+time.time() - while time.time() < timeisup: - count = 0 - summary = [] - for i, m in enumerate(targets): - motor, target, tolerance = m - #name = motor.short_name - #pend_event() - cur = motor.get_rbv() - done = motor.is_done() - #logger.debug("check {}[done={}]: {} == {}".format(name, done, cur, target)) - #summary.append("{}[done={}]: {} == {}".format(name, done, cur, target)) - if done and tolerance >= abs(cur - target): - count += 1 - #pend_event(0.1) + while time.time()=abs(cur-target): + count+=1 + pend_event(0.1) - if count == num_motors: - break - #pend_event(0.1) + if count==num_motors: + break + pend_event(0.1) + + if count!=num_motors: + raise PositionsNotReached("failed to reach target positions: {}".format("#".join(summary))) - if count != num_motors: - raise PositionsNotReached("failed to reach target positions: {}".format("#".join(summary))) def assert_motor_positions(targets, timeout=60.0): - """ - wait for each of the given motors to have specified positions within tolerance - :param targets: [ - (motor, target_position, tolerance), - ... - ] - :return: None - :raises: PositionsNotReached - """ - num_motors = len(targets) + """ + wait for each of the given motors to have specified positions within tolerance + :param targets: [ + (motor, target_position, tolerance), + ... + ] + :return: None + :raises: PositionsNotReached + """ + num_motors=len(targets) - timeisup = timeout + time.time() + timeisup=timeout+time.time() - while time.time() < timeisup: - count = 0 - summary = [] - for i, m in enumerate(targets): - motor, target, tolerance = m - name = motor._prefix - pend_event() - cur = motor.get_position(readback=True) - done = motor.done_moving - logger.debug("check {}[done={}]: {} == {}".format(name, done, cur, target)) - summary.append("{}[done={}]: {} == {}".format(name, done, cur, target)) - if done and tolerance >= abs(cur - target): - count += 1 - pend_event(0.1) + while time.time()=abs(cur-target): + count+=1 + pend_event(0.1) - if count == num_motors: - break - pend_event() + if count==num_motors: + break + pend_event() - if count != num_motors: - raise PositionsNotReached("failed to reach target positions: {}".format("#".join(summary))) + if count!=num_motors: + raise PositionsNotReached("failed to reach target positions: {}".format("#".join(summary))) diff --git a/backlight.py b/backlight.py index 203cd9d..bcef8bf 100755 --- a/backlight.py +++ b/backlight.py @@ -14,13 +14,13 @@ import epics #from app_utils import assert_motor_positions class Backlight(object): - def __init__(self, prefix: str='SAR-EXPMX:MOT_BLGT'): + def __init__(self, prefix: str='SAR-EXPMX:MOT_BLGT',pos={'pos_in':-29000,'pos_out':0,'pos_diode':0}): if prefix is None: self._sim={'pos':'out'} _log.info('simulated mode:{}'.format(self._sim)) return #simulated mode self._mot = epics.Motor(prefix) - self._pos = {'in':-29000,'out':0} + self._pos = pos def is_pos(self,strPos): try: @@ -29,7 +29,7 @@ class Backlight(object): _log.info('simulated mode:{}'.format(strPos)) return self._sim['pos']==strPos m.refresh() - return abs(m.readback - self._pos[strPos]) < 10 + return abs(m.readback - self._pos['pos_'+strPos]) < 10 def move(self, strPos, wait=False): try: @@ -40,7 +40,7 @@ class Backlight(object): return m=self._mot _log.debug("backlight to {}".format(strPos)) - target=self._pos[strPos] + target=self._pos['pos_'+strPos] m.move(target, ignore_limits=True, wait=wait) @@ -55,7 +55,7 @@ if __name__ == "__main__": parser.add_argument("-t", "--test", help="test sequence", action="store_true") args = parser.parse_args() - os.environ['EPICS_CA_ADDR_LIST'] = '129.129.244.255 sf-saresc-cagw.psi.ch:5062 sf-saresc-cagw.psi.ch:5066' + os.environ['EPICS_CA_ADDR_LIST']='129.129.244.255 sf-saresc-cagw.psi.ch:5062 sf-saresc-cagw.psi.ch:5066' _log.info('Arguments:{}'.format(args.__dict__)) bl = Backlight() diff --git a/camera.py b/camera.py index e4b3e3c..1450a76 100755 --- a/camera.py +++ b/camera.py @@ -217,7 +217,8 @@ class epics_cam(object): _log.info('simulated mode:{}'.format(exp)) self._sim['exp']=exp return - pv_exp.put(exp, wait=True) + self.update_params((pv_exp,exp)) + #pv_exp.put(exp, wait=True) def get_exposure(self): try: diff --git a/epics_widgets/MotorTweak.py b/epics_widgets/MotorTweak.py index 966dea1..a78c3f4 100644 --- a/epics_widgets/MotorTweak.py +++ b/epics_widgets/MotorTweak.py @@ -10,7 +10,7 @@ from PyQt5.uic import loadUiType from epics import Motor from epics.ca import pend_event -from app_utils import DeltatauMotorStatus, assert_motor_positions +from app_utils import assert_motor_positions Ui_MotorTweak, QWidget = loadUiType('epics_widgets/MotorTweak.ui') SPMG_STOP = 0 @@ -138,12 +138,6 @@ class MotorTweak(QWidget, Ui_MotorTweak): self._val=v=m.get('VAL') return v - def is_homed(self): - m=self._motor - m.refresh() - status = DeltatauMotorStatus(m.motor_status) - return bool(DeltatauMotorStatus.MOTOR_HAS_BEEN_HOMED & status) - def move_abs(self, drive=None, wait=False, assert_position=False): if drive is None: drive = float(self._drive_val.text()) diff --git a/epics_widgets/SmaractMotorTweak.py b/epics_widgets/SmaractMotorTweak.py index 7137f30..962cb7b 100644 --- a/epics_widgets/SmaractMotorTweak.py +++ b/epics_widgets/SmaractMotorTweak.py @@ -7,8 +7,7 @@ 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 #ZAC: orig. code +from motor_utils import assert_tweaker_positions Ui_MotorTweak, QWidget = loadUiType('epics_widgets/MotorTweak.ui') logger = logging.getLogger(__name__) diff --git a/psi_device.py b/psi_device.py index 1a2bc89..bee36d9 100644 --- a/psi_device.py +++ b/psi_device.py @@ -85,7 +85,7 @@ class Jungfrau: _log.warning(f'Jungfrau not connected: {e}') self._daq=None self._pv_pulse_id=epics.PV('SAR-EXPMX-EVR0:RX-PULSEID') - + self._pv_pulse_id.connect() def acquire(self,run_name, n_pulses, wait=False): if self._daq is not None: @@ -94,4 +94,6 @@ class Jungfrau: def gather_upload(self): self._pulse_id_end=self._pv_pulse_id.value + _log.debug(f'pulse_id: {self._pulse_id_start}..{self._pulse_id_end}') + diff --git a/pyqtUsrObj.py b/pyqtUsrObj.py index 56aedb9..f0e327b 100644 --- a/pyqtUsrObj.py +++ b/pyqtUsrObj.py @@ -20,6 +20,8 @@ from pyqtgraph.Qt import QtCore, QtGui import numpy as np from PyQt5.QtGui import QPolygon,QPolygonF from PyQt5.QtCore import Qt,QPointF,QLineF +from PyQt5.QtWidgets import QMenu + def itr2str(itr): return '('+', '.join(tuple(map(lambda x:f'{x:.6g}', itr)))+')' @@ -145,10 +147,28 @@ class Marker(pg.ROI): p.setTransform(tr) return ofx,ofy - -class Fiducial(pg.ROI): - def __init__(self, pos, size, z:float, **kargs): +class UsrROI(pg.ROI): + def __init__(self, pos, size, **kargs): pg.ROI.__init__(self, pos, size, **kargs) + + def cb_toggle_movable(self): + self.translatable=not self.translatable + + def contextMenuEvent(self, event): + #pg.ROI.contextMenuEvent(self,event) + _log.debug('ContextMenu') + menu = QMenu("ctx") + act=menu.addAction('locked') + act.setCheckable(True) + act.setChecked(not self.translatable) + act.triggered.connect(self.cb_toggle_movable) + #menu.addAction('center in view') + #menu.addAction('delete') + menu.exec(event.screenPos()) + +class Fiducial(UsrROI): + def __init__(self, pos, size, z:float, **kargs): + UsrROI.__init__(self, pos, size, **kargs) self._z=z def paint(self, p, *args): @@ -195,7 +215,7 @@ class Fiducial(pg.ROI): return jsn -class Grid(pg.ROI): +class Grid(UsrROI): '''a grid''' def __init__( self, pos=(0,0), size=(30,20), cnt=(6,4), fiducialSize=.2, **kargs): @@ -271,7 +291,7 @@ class Grid(pg.ROI): return pts -class Path(pg.ROI): +class Path(UsrROI): ''' a path object with fiducials path=np.array(n,2) of path points @@ -347,7 +367,7 @@ class Path(pg.ROI): } return jsn -class FixTargetFrame(pg.ROI): +class FixTargetFrame(UsrROI): '''fixed target frame''' tpl={ 'test':{ @@ -664,7 +684,7 @@ if __name__=='__main__': pass else: obj_info(o) - print(o.state) + #print(o.state) ## Create image to display @@ -689,7 +709,7 @@ if __name__=='__main__': w=pg.GraphicsLayoutWidget(show=True, size=(1000, 800), border=True) w.setWindowTitle('pyqtgraph example: ROI Examples') - vb=w.addViewBox(row=1, col=0, lockAspect=True,invertY=False) + vb=w.addViewBox(row=1, col=0, lockAspect=True,invertY=False,enableMenu=False) vb.enableAutoRange(enable=False) try: g=pg.GridItem(pen=(0, 255, 0), textPen=(0, 255, 0)) # green grid and labels @@ -748,9 +768,13 @@ if __name__=='__main__': vi=FixTargetFrame((400,-200),(400,400),tpl='12.5x12.5') vb.addItem(vi) - vi=Fiducial((0,200),(40,40),3) + vi=Fiducial((0,200),(40,40),3,movable=False,removable=True) vb.addItem(vi) + vi=pg.PolyLineROI([(22, -19), (40, -30), (23, -10), (22, -19)], closed=True) + vb.addItem(vi) + + viRoi=pg.ROI([-200, -200], [100, 80],movable=True, rotatable=True, resizable=True) viRoi.addFreeHandle(pos=[.7, .5], axes=None, item=None, name=None, index=None) # rechteck , frei beweglich ??? verschwinden anch bewegung diff --git a/swissmx.py b/swissmx.py index 4216c21..fb5c9fa 100755 --- a/swissmx.py +++ b/swissmx.py @@ -5,10 +5,10 @@ # | Based on Zac great first implementation | # | Author Thierry Zamofing (thierry.zamofing@psi.ch) | # *-----------------------------------------------------------------------* -''' +""" SwissMx experiment application. Based on Zac great first implementation - + bitmask for simulation: 0x01: backlight @@ -18,7 +18,7 @@ bitmask for simulation: 0x10: Deltatau motors 0x20: SmarAct motors -''' +""" import logging logging.basicConfig(level=logging.DEBUG, format='%(levelname)s:%(module)s:%(lineno)d:%(funcName)s:%(message)s ') @@ -439,9 +439,6 @@ class WndSwissMx(QMainWindow, Ui_MainWindow): self.shortcut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_S), self) self.shortcut.activated.connect(self.cb_save_cam_image) - self.shortcut = QShortcut(QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_T), self) - self.shortcut.activated.connect(lambda: qutilities.toggle_warn(SKIP_ESCAPE_TRANSITIONS_IF_SAFE)) - for k in range(10): qkey = k + Qt.Key_0 self.shortcut = QShortcut(QKeySequence(Qt.CTRL + Qt.SHIFT + qkey), self) @@ -632,11 +629,12 @@ class WndSwissMx(QMainWindow, Ui_MainWindow): _log.debug('TODO: check to not connect smultiple igNewCamImg') if index > 0: # not showing camera image _log.warning("listening to zescape") - self.timer.stop() - zescape.start_listening() - self.timer = QtCore.QTimer(self) - self.timer.timeout.connect(self.check_zescape) - self.timer.start(20) + pass + #self.timer.stop() + #zescape.start_listening() + #self.timer = QtCore.QTimer(self) + #self.timer.timeout.connect(self.check_zescape) + #self.timer.start(20) else: app=QApplication.instance() try: @@ -1461,15 +1459,16 @@ class WndSwissMx(QMainWindow, Ui_MainWindow): vb.removeItem(go) tmpGoLst.clear() - n=int(p.shape[0]/100)+1 - for i in range(0,p.shape[0],n): - fx,fy=p[i, :]/1000 - fx=-fx #X axis has inverted sign ! - l=.06 - go=UsrGO.Fiducial((fx-l/2, fy-l/2), (l, l), i) - grp.addItem(go) - tmpGoLst.append(go) - mft._tree.setData(grp.childItems()) + # adding debug fiducials + #n=int(p.shape[0]/100)+1 + #for i in range(0,p.shape[0],n): + # fx,fy=p[i, :]/1000 + # fx=-fx #X axis has inverted sign ! + # l=.06 + # go=UsrGO.Fiducial((fx-l/2, fy-l/2), (l, l), i) + # grp.addItem(go) + # tmpGoLst.append(go) + #mft._tree.setData(grp.childItems()) #_log.debug(f'first point:{p[0,:]}') #_log.debug(f'step to 2nd point:{p[1,:]-p[0,:]}') @@ -1541,7 +1540,7 @@ class WndSwissMx(QMainWindow, Ui_MainWindow): app=QApplication.instance() steps = [ # lambda: sample_selection.tell.set_current(30.0), - lambda: self.move_collimator("ready"), + lambda: self.move_collimator("out"), lambda:self.move_detector("out"), lambda:self.move_post_tube("out"), lambda:app._backlight.move("in"), @@ -2080,14 +2079,20 @@ Author Thierry Zamofing (thierry.zamofing@psi.ch) j=0 elif type(go)==UsrGO.Fiducial and go.size()[0]==0.12: - ptFitTrf[j]=go.pos()+go.size()/2 + if j=3: geo.least_square_plane(ptFitPlane) + def cb_progress(self,i,sz,dlg): + dlg.setValue(int(sz*90/i)) + if dlg.wasCanceled(): + raise AttributeError('canceled') def daq_collect(self, **kwargs):# points, visualizer_method, visualizer_params): #def _OLD_daq_collect_points(self, points, visualizer_method, visualizer_params): @@ -2134,14 +2139,21 @@ Author Thierry Zamofing (thierry.zamofing@psi.ch) sp.setup_coord_trf() # reset to shape path system # sp.meta['pt2pt_time']=10 #put between setup_sync and setup_motion to have more motion points than FEL syncs dlg.setLabelText("Download motion program");dlg+=5 + + try: + sp.comm.gpascii._cb_func=lambda i, sz:self.cb_progress(i, sz, dlg) + except AttributeError: + pass + sp.setup_motion(fnPrg=fn+'.prg', mode=3, scale=1., dwell=10) - dlg.setLabelText("Homing and get ready");dlg+=35 + if dlg.wasCanceled(): + return + dlg.setLabelText("Homing and get ready");dlg+=5 sp.homing() # homing if needed sp.run() # start motion program sp.wait_armed() # wait until motors are at first position + sp.trigger(0.5) # send a start trigger (if needed) after given time - #PV with the current pulse - # [sf-lc7a ~]$ caget jf.acquire('filename.tmp',sp.points.shape[0]) if not dt._comm is None: dlg.setLabelText("run motion/acquisition") @@ -3906,10 +3918,13 @@ if __name__=="__main__": app._geometry._opt_ctr=cfg.value(cfg.GEO_OPT_CTR) startupWin.set(15, f'connect backlight') + pfx=app._cfg.value(AppCfg.GBL_DEV_PREFIX)[0] + dft_pos_bklgt=app._cfg.value(AppCfg.DFT_POS_BKLGT) + if args.sim&0x01: - app._backlight = backlight.Backlight(None) + app._backlight = backlight.Backlight(None,dft_pos_bklgt) else: - app._backlight = backlight.Backlight() + app._backlight = backlight.Backlight(f'{pfx}:MOT_BLGT',dft_pos_bklgt) startupWin.set(20, f'connect illumination') if args.sim&0x02: app._illumination = illumination.IlluminationControl(None)