Files
John Henry BealeandClaude Opus 5.5 e05ebeb76b run blocking motor sequences in the worker thread, wait for the backlight
Detector, collimator, post-tube in/out, move home/yag, the backlight button
and the sample exchange/alignment/data collection transitions blocked the
GUI thread for up to ~20s per move.

- MoveWorker (same thread as the collection worker, so sequences and
  collections can not overlap) runs (label, callable) steps with a progress
  dialog; all controls are locked meanwhile (no nudges, zoom stays usable)
- move functions raise MoveError instead of showing dialogs; the GUI shows
  the message when the sequence ends
- abort stops all motors (motor record STOP, backlight) and ends the
  sequence; position checks (assert_tweaker_positions abort=) stop waiting
- safety: backlight moves wait and check the position before the next step
  (post-tube in used to start while the backlight was still moving out)
- tandem nudges stay non blocking in the GUI thread; collimator still moves
  X then Y
- sample alignment: backlight in now also checks the detector is clear of
  the post-tube path (as the backlight button does)

Tested in simulation and at the beamline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 09:20:51 +02:00

73 lines
2.3 KiB
Python
Executable File

#!/usr/bin/env python
# *-----------------------------------------------------------------------*
# | |
# | Copyright (c) 2022 by Paul Scherrer Institute (http://www.psi.ch) |
# | Based on Zac great first implementation |
# | Author Thierry Zamofing (thierry.zamofing@psi.ch) |
# *-----------------------------------------------------------------------*
import logging
_log=logging.getLogger(__name__)
import epics
class Backlight(object):
def __init__(self, prefix: str='SAR-EXPMX:MOT_BLGT',pos={'pos_in':-30500,'pos_out':1000,'pos_diode':-30500}):
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 = pos
def is_pos(self,strPos):
try:
m=self._mot
except AttributeError:
_log.info('simulated mode:{}'.format(strPos))
return self._sim['pos']==strPos
m.refresh()
return abs(m.readback - self._pos['pos_'+strPos]) < 10
def stop(self):
try:
self._mot.stop()
except AttributeError:
pass #simulated mode
def move(self, strPos, wait=False):
try:
m=self._mot
except AttributeError:
_log.info('simulated mode:{}'.format(strPos))
self._sim['pos']=strPos
return
m=self._mot
_log.debug("backlight to {}".format(strPos))
target=self._pos['pos_'+strPos]
m.move(target, ignore_limits=True, wait=wait)
if __name__ == "__main__":
import time,os
import argparse
logging.basicConfig(level=logging.DEBUG,format='%(levelname)s:%(module)s:%(lineno)d:%(funcName)s:%(message)s ')
parser = argparse.ArgumentParser()
parser.add_argument('-m', '--mode', help='mode string with characters: "frgbka" for front,red,green,blue,back,all')
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'
_log.info('Arguments:{}'.format(args.__dict__))
bl = Backlight()
if args.test:
bl.move('in')
time.sleep(2)
bl.move('out')
time.sleep(2)
bl.move('in')
time.sleep(2)
bl.move('out')
time.sleep(2)