From fac13359a1c527f24673b749ed5150bd1d12aa01 Mon Sep 17 00:00:00 2001 From: prjemian Date: Mon, 18 Jun 2018 10:46:04 -0500 Subject: [PATCH] fixes #33, fixes #34 --- APS_BlueSky_tools/devices.py | 94 ++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/APS_BlueSky_tools/devices.py b/APS_BlueSky_tools/devices.py index 39c08b1b3..1f6bb2902 100644 --- a/APS_BlueSky_tools/devices.py +++ b/APS_BlueSky_tools/devices.py @@ -113,6 +113,7 @@ class ApsPssShutter(Device): * APS PSS shutters have separate bit PVs for open and close * set either bit, the shutter moves, and the bit resets a short time later * no indication that the shutter has actually moved from the bits + (see :func:`ApsPssShutterWithStatus()` for alternative) USAGE: @@ -196,6 +197,99 @@ def run_and_delay(): return status +class ApsPssShutterWithStatus(Device): + """ + APS PSS shutter + + * APS PSS shutters have separate bit PVs for open and close + * set either bit, the shutter moves, and the bit resets a short time later + * a separate status PV tells if the shutter is open or closed + (see :func:`ApsPssShutter()` for alternative) + + USAGE: + + shutter_a = ApsPssShutterWithStatus("2bma:A_shutter", name="shutter") + + shutter_a.open() + shutter_a.close() + + or + + %mov shutter_a "open" + %mov shutter_a "close" + + or + + shutter_a.set("open") # MUST be "open", not "Open" + shutter_a.set("close") + + When using the shutter in a plan, be sure to use `yield from`. + + def in_a_plan(shutter): + yield from abs_set(shutter, "open", wait=True) + # do something + yield from abs_set(shutter, "close", wait=True) + + RE(in_a_plan(shutter_a)) + + The strings accepted by `set()` are defined in attributes + (`open_str` and `close_str`). + """ + open_bit = Component(EpicsSignal, ":open") + close_bit = Component(EpicsSignal, ":close") + delay_s = 1.2 + pss_state = FormattedComponent(EpicsSignalRO, "{self.state_pv}") + + # strings the user will use + open_str = 'open' + close_str = 'close' + + # pss_state PV values from EPICS + open_val = 1 + close_val = 0 + + def __init__(self, prefix, state_pv, *args, **kwargs): + self.state_pv = state_pv + super().__init__(prefix, *args, **kwargs) + + def open(self, timeout=10): + ophyd.status.wait(self.set(self.open_str), timeout=timeout) + + def close(self, timeout=10): + ophyd.status.wait(self.set(self.close_str), timeout=timeout) + + def set(self, value, **kwargs): + # first, validate the input value + acceptables = (self.close_str, self.open_str) + if value not in acceptables: + msg = "value should be one of " + " | ".join(acceptables) + msg += " : received " + str(value) + raise ValueError(msg) + + command_signal = { + self.open_str: self.open_bit, + self.close_str: self.close_bit + }[value] + expected_value = { + self.open_str: self.open_val, + self.close_str: self.close_val + }[value] + + working_status = DeviceStatus(self) + + def shutter_cb(value, timestamp, **kwargs): + # APS shutter state PVs do not define strings, use numbers + #value = enums[int(value)] + value = int(value) + if value == expected_value: + self.pss_state.clear_sub(shutter_cb) + working_status._finished() + + self.pss_state.subscribe(shutter_cb) + command_signal.set(1) + return working_status + + class AxisTunerException(ValueError): """Exception during execution of `AxisTunerBase` subclass""" pass