diff --git a/startup/11-txm_motor.py b/startup/11-txm_motor.py index eb46842..4c01279 100755 --- a/startup/11-txm_motor.py +++ b/startup/11-txm_motor.py @@ -1,7 +1,92 @@ from ophyd import EpicsMotor, EpicsSignalRO, Device, Component as Cpt from ophyd import EpicsSignal -from nslsii.devices import TwoButtonShutter + +class TwoButtonShutter(Device): + # vendored from nslsii.devices to extend timeouts/retries + RETRY_PERIOD = 0.5 # seconds + MAX_ATTEMPTS = 120 + # TODO: this needs to be fixed in EPICS as these names make no sense + # the value coming out of the PV does not match what is shown in CSS + open_cmd = Cpt(EpicsSignal, 'Cmd:Opn-Cmd', string=True) + open_val = 'Open' + + close_cmd = Cpt(EpicsSignal, 'Cmd:Cls-Cmd', string=True) + close_val = 'Not Open' + + status = Cpt(EpicsSignalRO, 'Pos-Sts', string=True) + fail_to_close = Cpt(EpicsSignalRO, 'Sts:FailCls-Sts', string=True) + fail_to_open = Cpt(EpicsSignalRO, 'Sts:FailOpn-Sts', string=True) + enabled_status = Cpt(EpicsSignalRO, 'Enbl-Sts', string=True) + + # user facing commands + open_str = 'Open' + close_str = 'Close' + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._set_st = None + + def set(self, val): + if self._set_st is not None: + raise RuntimeError(f'trying to set {self.name}' + ' while a set is in progress') + + cmd_map = {self.open_str: self.open_cmd, + self.close_str: self.close_cmd} + target_map = {self.open_str: self.open_val, + self.close_str: self.close_val} + + cmd_sig = cmd_map[val] + target_val = target_map[val] + + st = DeviceStatus(self) + if self.status.get() == target_val: + st._finished() + return st + + self._set_st = st + print(self.name, val, id(st)) + enums = self.status.enum_strs + + def shutter_cb(value, timestamp, **kwargs): + value = enums[int(value)] + if value == target_val: + self._set_st = None + self.status.clear_sub(shutter_cb) + st._finished() + + cmd_enums = cmd_sig.enum_strs + count = 0 + + def cmd_retry_cb(value, timestamp, **kwargs): + nonlocal count + value = cmd_enums[int(value)] + count += 1 + if count > self.MAX_ATTEMPTS: + cmd_sig.clear_sub(cmd_retry_cb) + self._set_st = None + self.status.clear_sub(shutter_cb) + st._finished(success=False) + if value == 'None': + if not st.done: + time.sleep(self.RETRY_PERIOD) + cmd_sig.set(1) + + ts = datetime.datetime.fromtimestamp(timestamp) \ + .strftime(_time_fmtstr) + if count > 2: + msg = '** ({}) Had to reactuate shutter while {}ing' + print(msg.format(ts, val if val != 'Close' + else val[:-1])) + else: + cmd_sig.clear_sub(cmd_retry_cb) + + cmd_sig.subscribe(cmd_retry_cb, run=False) + self.status.subscribe(shutter_cb) + cmd_sig.set(1) + + return st class MyBaseMotor(EpicsMotor): diff --git a/startup/40-scans.py b/startup/40-scans.py index fc11100..3c23630 100755 --- a/startup/40-scans.py +++ b/startup/40-scans.py @@ -82,6 +82,10 @@ def _move_sample_in(in_x, in_y, in_z, in_r, repeat=1, trans_first_flag=1): # #yield from bps.sleep(1) +def _close_shutter(simu=False): + yield from mv(shutter, 'Close') + +''' def _close_shutter(simu=False): if simu: print("testing: close shutter") @@ -103,8 +107,10 @@ def _close_shutter(simu=False): break # yield from abs_set(shutter_close, 1) # yield from bps.sleep(1) - - +''' +def _open_shutter(simu=False): + yield from mv(shutter, 'Open') +''' def _open_shutter(simu=False): if simu: print("testing: open shutter") @@ -123,7 +129,7 @@ def _open_shutter(simu=False): break # yield from abs_set(shutter_open, 1) # yield from bps.sleep(1) - +''' def _set_andor_param(exposure_time=0.1, period=0.1, chunk_size=1): # yield from mv(Andor.cam.acquire, 0)