def test_epicssignal_get_in_callback(fake_motor_ioc, cleanup): pvs = fake_motor_ioc.pvs sig = EpicsSignal(write_pv=pvs['setpoint'], read_pv=pvs['readback'], name='motor') cleanup.add(sig) called = [] def generic_sub(sub_type, **kwargs): called.append((sub_type, sig.get(), sig.get_setpoint())) for event_type in (sig.SUB_VALUE, sig.SUB_META, sig.SUB_SETPOINT, sig.SUB_SETPOINT_META): sig.subscribe(generic_sub, event_type=event_type) sig.wait_for_connection() sig.put(1, wait=True) sig.put(2, wait=True) time.sleep(0.5) print(called) # Arbitrary threshold, but if @klauer screwed something up again, this will # blow up assert len(called) < 20 print('total', len(called)) sig.unsubscribe_all()
def test_epicssignal_get_in_callback(cleanup, fake_motor_ioc): pvs = fake_motor_ioc.pvs sig = EpicsSignal(write_pv=pvs['setpoint'], read_pv=pvs['readback'], name='motor') cleanup.add(sig) called = [] def generic_sub(sub_type, **kwargs): called.append((sub_type, sig.get(), sig.get_setpoint())) for event_type in (sig.SUB_VALUE, sig.SUB_META, sig.SUB_SETPOINT, sig.SUB_SETPOINT_META): sig.subscribe(generic_sub, event_type=event_type) sig.wait_for_connection() sig.put(1, wait=True) sig.put(2, wait=True) time.sleep(0.5) print(called) # Arbitrary threshold, but if @klauer screwed something up again, this will # blow up assert len(called) < 20 print('total', len(called))
def test_epicssignal_sub_setpoint(cleanup, fake_motor_ioc): pvs = fake_motor_ioc.pvs pv = EpicsSignal(write_pv=pvs['setpoint'], read_pv=pvs['readback'], name='pv') cleanup.add(pv) setpoint_called = [] setpoint_meta_called = [] def sub_setpoint(old_value, value, **kwargs): setpoint_called.append((old_value, value)) def sub_setpoint_meta(timestamp, **kwargs): setpoint_meta_called.append(timestamp) pv.subscribe(sub_setpoint, event_type=pv.SUB_SETPOINT) pv.subscribe(sub_setpoint_meta, event_type=pv.SUB_SETPOINT_META) pv.wait_for_connection() pv.put(1, wait=True) pv.put(2, wait=True) time.sleep(1.0) assert len(setpoint_called) >= 3 assert len(setpoint_meta_called) >= 3
def test_epicssignal_sub_setpoint(cleanup, fake_motor_ioc): pvs = fake_motor_ioc.pvs pv = EpicsSignal(write_pv=pvs['setpoint'], read_pv=pvs['readback'], name='pv') cleanup.add(pv) setpoint_called = [] setpoint_meta_called = [] def sub_setpoint(old_value, value, **kwargs): setpoint_called.append((old_value, value)) def sub_setpoint_meta(timestamp, **kwargs): setpoint_meta_called.append(timestamp) pv.subscribe(sub_setpoint, event_type=pv.SUB_SETPOINT) pv.subscribe(sub_setpoint_meta, event_type=pv.SUB_SETPOINT_META) pv.wait_for_connection() pv.put(1, wait=True) pv.put(2, wait=True) time.sleep(0.5) assert len(setpoint_called) >= 3 assert len(setpoint_meta_called) >= 3
def test_epicssignal_waveform(): def update_cb(value=None, **kwargs): assert value in FakeEpicsWaveform.strings signal = EpicsSignal('readpv', string=True) signal.wait_for_connection() signal.subscribe(update_cb, event_type=signal.SUB_VALUE) assert signal.value in FakeEpicsWaveform.strings
def test_epicssignal_waveform(self): epics.PV = FakeEpicsWaveform def update_cb(value=None, **kwargs): self.assertIn(value, FakeEpicsWaveform.strings) signal = EpicsSignal('readpv', string=True) signal.wait_for_connection() signal.subscribe(update_cb) self.assertIn(signal.value, FakeEpicsWaveform.strings)
def test_epicssignal_waveform(cleanup, signal_test_ioc): def update_cb(value=None, **kwargs): assert len(value) > 1 signal = EpicsSignal(signal_test_ioc.pvs['waveform'], string=True) cleanup.add(signal) signal.wait_for_connection() sub = signal.subscribe(update_cb, event_type=signal.SUB_VALUE) assert len(signal.value) > 1 signal.unsubscribe(sub)
def test_epicssignal_waveform(cleanup, signal_test_ioc): called = False def update_cb(value=None, **kwargs): nonlocal called assert len(value) > 1 called = True signal = EpicsSignal(signal_test_ioc.pvs['waveform'], string=True) cleanup.add(signal) signal.wait_for_connection() sub = signal.subscribe(update_cb, event_type=signal.SUB_VALUE) assert len(signal.get()) > 1 # force the current thread to allow other threads to run to service # subscription time.sleep(0.2) assert called signal.unsubscribe(sub)
class _OptionalEpicsSignal(Signal): """ An EPICS Signal which may or may not exist. The init parameters mirror those of :class:`~ophyd.EpicsSignal`. Notes ----- This should be considered for internal use only, and not for user-facing device components. If you use this in your new device, there is a good chance we will reject your PR. """ def __init__(self, read_pv, write_pv=None, *, name, parent=None, kind=None, **kwargs): self._saw_connection = False self._epics_signal = EpicsSignal(read_pv=read_pv, write_pv=write_pv, parent=self, name=name, kind=kind, **kwargs) super().__init__(name=name, parent=parent, kind=kind) self._epics_signal.subscribe( self._epics_meta_update, event_type=self._epics_signal.SUB_META, ) def _epics_value_update(self, **kwargs): """The EpicsSignal value updated.""" super().put(value=kwargs['value'], timestamp=kwargs['timestamp'], force=True) # Note: the above internally calls run_subs # self._run_subs(**kwargs) def _epics_meta_update(self, sub_type=None, **kwargs): """The EpicsSignal metadata updated; reflect that here.""" self._metadata.update(**kwargs) self._run_subs(sub_type=self.SUB_META, **kwargs) if not self._saw_connection and kwargs.get('connected', False): self._epics_signal.subscribe(self._epics_value_update) self._saw_connection = True def destroy(self): super().destroy() self._epics_signal.destroy() self._epics_signal = None def should_use_epics_signal(self) -> bool: """ Tell `_OptionalEpicsSignal` whether or not to use the `EpicsSignal`. By default, the `EpicsSignal` will be used if the PV has connected. Note ---- * Subclasses should override this with their own functionality. * This value should not change during the lifetime of the `_OptionalEpicsSignal`. """ return self._saw_connection def _proxy_method(method_name): # noqa """ Proxy a method from either the EpicsSignal or the superclass Signal. """ def method_selector(self, *args, **kwargs): owner = (self._epics_signal if self.should_use_epics_signal() else super()) return getattr(owner, method_name)(*args, **kwargs) return method_selector describe = _proxy_method('describe') describe_configuration = _proxy_method('describe_configuration') get = _proxy_method('get') put = _proxy_method('put') set = _proxy_method('set') read = _proxy_method('read') read_configuration = _proxy_method('read_configuration') wait_for_connection = _proxy_method('wait_for_connection') def _proxy_property(prop_name, value): # noqa """Read-only property proxy for the internal EPICS Signal.""" def getter(self): if self.should_use_epics_signal(): return getattr(self._epics_signal, prop_name) return value # Only support read-only properties for now. return property(getter) connected = _proxy_property('connected', True) read_access = _proxy_property('read_access', True) write_access = _proxy_property('write_access', True) precision = _proxy_property('precision', 4) enum_strs = _proxy_property('enum_strs', ()) limits = _proxy_property('limits', (0, 0)) @property def kind(self): """The EPICS signal's kind.""" return self._epics_signal.kind @kind.setter def kind(self, value): self._epics_signal.kind = value