From 9f589af737380a2950d112af2660a9f998f7fba5 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 21 Dec 2020 22:57:09 +0100 Subject: [PATCH 01/82] Add emulated HW RoI to dummy 2D controller --- .../poolcontrollers/DummyTwoDController.py | 56 ++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/src/sardana/pool/poolcontrollers/DummyTwoDController.py b/src/sardana/pool/poolcontrollers/DummyTwoDController.py index fdd543b933..22b699d103 100644 --- a/src/sardana/pool/poolcontrollers/DummyTwoDController.py +++ b/src/sardana/pool/poolcontrollers/DummyTwoDController.py @@ -107,6 +107,7 @@ def __init__(self, idx): self.saving_enabled = False self.value_ref_pattern = "h5file:///tmp/dummy2d_default_{index}.h5" self.value_ref_enabled = False + self.roi = [0, 0, 0, 0] class BaseValue(object): @@ -164,7 +165,16 @@ class BasicDummyTwoDController(TwoDController): FSet: 'setAmplitude', Description: ("Amplitude. Maybe a number or a tango attribute " "(must start with tango://)"), - DefaultValue: '1.0'} + DefaultValue: '1.0' + }, + 'RoI': { + Type: (int,), + FGet: 'getRoI', + FSet: 'setRoI', + Description: ("Region of Interest of image " + "(begin_x, end_x, begin_y, end_y)"), + DefaultValue: [0, 0, 0, 0] + } } def __init__(self, inst, props, *args, **kwargs): @@ -284,6 +294,9 @@ def _updateChannelValue(self, axis, elapsed_time): y_size = self.BufferSize[1] amplitude = axis * self.integ_time * channel.amplitude.get() img = generate_img(x_size, y_size, amplitude) + roi = channel.roi + if roi != [0, 0, 0, 0]: + img = img[roi[0]:roi[1], roi[2]:roi[3]] if self._synchronization == AcqSynch.SoftwareTrigger: channel.value = img channel.acq_idx += 1 @@ -368,6 +381,44 @@ def setAmplitude(self, axis, value): klass = TangoValue channel.amplitude = klass(value) + def getRoI(self, axis): + idx = axis - 1 + channel = self.channels[idx] + return channel.roi + + def setRoI(self, axis, value): + idx = axis - 1 + channel = self.channels[idx] + try: + value = value.tolist() + except AttributeError: + pass + if len(value) != 4: + raise ValueError("RoI is not a list of four elements") + if any(not isinstance(v, int) for v in value): + raise ValueError("RoI is not a list of integers") + if value[1] <= value[0]: + raise ValueError("RoI[1] is lower or equal than RoI[0]") + if value[3] <= value[2]: + raise ValueError("RoI[3] is lower or equal than RoI[2]") + x_dim = self.BufferSize[0] + if value[0] > (x_dim - 1): + raise ValueError( + "RoI[0] exceeds detector X dimension - 1 ({})".format( + x_dim - 1)) + if value[1] > x_dim: + raise ValueError( + "RoI[1] exceeds detector X dimension ({})".format(x_dim)) + y_dim = self.BufferSize[1] + if value[2] > (y_dim - 1): + raise ValueError( + "RoI[2] exceeds detector Y dimension - 1 ({})".format( + y_dim - 1)) + if value[3] > y_dim: + raise ValueError( + "RoI[3] exceeds detector Y dimension ({})".format(y_dim)) + channel.roi = value + def GetCtrlPar(self, par): if par == "synchronization": return self._synchronization @@ -488,6 +539,9 @@ def _updateChannelValue(self, axis, elapsed_time): y_size = self.BufferSize[1] amplitude = axis * self.integ_time * channel.amplitude.get() img = generate_img(x_size, y_size, amplitude) + roi = channel.roi + if roi != [0, 0, 0, 0]: + img = img[roi[0]:roi[1], roi[2]:roi[3]] if self._synchronization == AcqSynch.SoftwareTrigger: channel.value = img if channel.value_ref_enabled: From 684d4d569e85dd5c995582c0a622a3a369dc4987 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 21 Dec 2020 23:32:28 +0100 Subject: [PATCH 02/82] Add emulated HW RoI to dummy 1D controller --- .../poolcontrollers/DummyOneDController.py | 44 ++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/src/sardana/pool/poolcontrollers/DummyOneDController.py b/src/sardana/pool/poolcontrollers/DummyOneDController.py index 88f4e38ac5..d962408792 100644 --- a/src/sardana/pool/poolcontrollers/DummyOneDController.py +++ b/src/sardana/pool/poolcontrollers/DummyOneDController.py @@ -44,6 +44,7 @@ def __init__(self, idx): self.active = True self.amplitude = BaseValue('1.0') self._counter = 0 + self.roi = [0, 0] class BaseValue(object): @@ -89,7 +90,15 @@ class DummyOneDController(OneDController): FGet: 'getAmplitude', FSet: 'setAmplitude', Description: 'Amplitude. Maybe a number or a tango attribute(must start with tango://)', - DefaultValue: '1.0'}, + DefaultValue: '1.0' + }, + 'RoI': { + Type: (int,), + FGet: 'getRoI', + FSet: 'setRoI', + Description: "Region of Interest of spectrum (begin, end)", + DefaultValue: [0, 0] + } } def __init__(self, inst, props, *args, **kwargs): @@ -170,7 +179,11 @@ def _updateChannelValue(self, axis, elapsed_time): t = self.integ_time x = numpy.linspace(-10, 10, self.BufferSize[0]) amplitude = axis * t * channel.amplitude.get() - channel.value = gauss(x, 0, amplitude, 4) + spectrum = gauss(x, 0, amplitude, 4) + roi = channel.roi + if roi != [0, 0]: + spectrum = spectrum[roi[0]:roi[1]] + channel.value = spectrum elif self._synchronization in (AcqSynch.HardwareTrigger, AcqSynch.HardwareGate): if self.integ_time is not None: @@ -277,3 +290,30 @@ def setAmplitude(self, axis, value): if value.startswith("tango://"): klass = TangoValue channel.amplitude = klass(value) + + def getRoI(self, axis): + idx = axis - 1 + channel = self.channels[idx] + return channel.roi + + def setRoI(self, axis, value): + idx = axis - 1 + channel = self.channels[idx] + try: + value = value.tolist() + except AttributeError: + pass + if len(value) != 2: + raise ValueError("RoI is not a list of two elements") + if any(not isinstance(v, int) for v in value): + raise ValueError("RoI is not a list of integers") + if value[1] <= value[0]: + raise ValueError("RoI[1] is lower or equal than RoI[0]") + dim = self.BufferSize[0] + if value[0] > (dim - 1): + raise ValueError( + "RoI[0] exceeds detector dimension - 1 ({})".format(dim - 1)) + if value[1] > dim: + raise ValueError( + "RoI[1] exceeds detector dimension ({})".format(dim)) + channel.roi = value From 36ede5b93c4a239f75c22511aee01ceab557f3d6 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 21 Dec 2020 23:48:02 +0100 Subject: [PATCH 03/82] Allow to reset RoI of 1D and 2D --- src/sardana/pool/poolcontrollers/DummyOneDController.py | 2 +- src/sardana/pool/poolcontrollers/DummyTwoDController.py | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/sardana/pool/poolcontrollers/DummyOneDController.py b/src/sardana/pool/poolcontrollers/DummyOneDController.py index d962408792..106c3754c6 100644 --- a/src/sardana/pool/poolcontrollers/DummyOneDController.py +++ b/src/sardana/pool/poolcontrollers/DummyOneDController.py @@ -307,7 +307,7 @@ def setRoI(self, axis, value): raise ValueError("RoI is not a list of two elements") if any(not isinstance(v, int) for v in value): raise ValueError("RoI is not a list of integers") - if value[1] <= value[0]: + if value != [0, 0] and value[1] <= value[0]: raise ValueError("RoI[1] is lower or equal than RoI[0]") dim = self.BufferSize[0] if value[0] > (dim - 1): diff --git a/src/sardana/pool/poolcontrollers/DummyTwoDController.py b/src/sardana/pool/poolcontrollers/DummyTwoDController.py index 22b699d103..9b34c1dc71 100644 --- a/src/sardana/pool/poolcontrollers/DummyTwoDController.py +++ b/src/sardana/pool/poolcontrollers/DummyTwoDController.py @@ -397,10 +397,11 @@ def setRoI(self, axis, value): raise ValueError("RoI is not a list of four elements") if any(not isinstance(v, int) for v in value): raise ValueError("RoI is not a list of integers") - if value[1] <= value[0]: - raise ValueError("RoI[1] is lower or equal than RoI[0]") - if value[3] <= value[2]: - raise ValueError("RoI[3] is lower or equal than RoI[2]") + if value != [0, 0, 0, 0]: + if value[1] <= value[0]: + raise ValueError("RoI[1] is lower or equal than RoI[0]") + if value[3] <= value[2]: + raise ValueError("RoI[3] is lower or equal than RoI[2]") x_dim = self.BufferSize[0] if value[0] > (x_dim - 1): raise ValueError( From 844de3469425021222e68e63470143571f77b6b6 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 23 Dec 2020 00:18:17 +0100 Subject: [PATCH 04/82] Add shape concept to experimental channels Shape is a result of channel's configuration e.g. RoI, binning. The idea is to remove the shape from the measurement group configuration. Add: - shape property on the kernel level - implement backwards compatibility for 1D and 2D controllers not implementing shape - add Shape read-only Tango attribute --- src/sardana/pool/controller.py | 12 +++++++ src/sardana/pool/poolbasechannel.py | 48 +++++++++++++++++++++++++++- src/sardana/tango/pool/PoolDevice.py | 18 +++++++++-- 3 files changed, 75 insertions(+), 3 deletions(-) diff --git a/src/sardana/pool/controller.py b/src/sardana/pool/controller.py index 4895651f8f..e8d3282d42 100644 --- a/src/sardana/pool/controller.py +++ b/src/sardana/pool/controller.py @@ -878,6 +878,8 @@ class CounterTimerController(Controller, Readable, Startable, Stopable, # TODO: in case of Tango ValueBuffer type is overridden by DevEncoded 'ValueBuffer': {'type': str, 'description': 'Value buffer', }, + 'Shape': {'type': (int,), + 'description': 'Shape of the value, it is an empty array'} } standard_axis_attributes.update(Controller.standard_axis_attributes) @@ -940,6 +942,8 @@ class ZeroDController(Controller, Readable, Stopable): # TODO: in case of Tango ValueBuffer type is overridden by DevEncoded 'ValueBuffer': {'type': str, 'description': 'Value buffer', }, + 'Shape': {'type': (int,), + 'description': 'Shape of the value, it is an empty array'} } standard_axis_attributes.update(Controller.standard_axis_attributes) @@ -972,6 +976,9 @@ class OneDController(Controller, Readable, Startable, Stopable, Loadable): # TODO: in case of Tango ValueBuffer type is overridden by DevEncoded 'ValueBuffer': {'type': str, 'description': 'Value buffer', }, + 'Shape': {'type': (int,), + 'description': 'Shape of the value, it is an array with ' + '1 element - X dimension'} } standard_axis_attributes.update(Controller.standard_axis_attributes) @@ -1017,6 +1024,9 @@ class TwoDController(Controller, Readable, Startable, Stopable, Loadable): # TODO: in case of Tango ValueBuffer type is overridden by DevEncoded 'ValueBuffer': {'type': str, 'description': 'Value buffer', }, + 'Shape': {'type': (int,), + 'description': 'Shape of the value, it is an array with ' + '2 elements: X and Y dimensions'} } standard_axis_attributes.update(Controller.standard_axis_attributes) @@ -1263,6 +1273,8 @@ class PseudoCounterController(Controller): # TODO: in case of Tango ValueBuffer type is overridden by DevEncoded 'ValueBuffer': {'type': str, 'description': 'Data', }, + 'Shape': {'type': (int,), + 'description': 'Shape of the value, it is an empty array'} } #: A :obj:`str` representing the controller gender diff --git a/src/sardana/pool/poolbasechannel.py b/src/sardana/pool/poolbasechannel.py index 29f739bad9..a644cbc410 100644 --- a/src/sardana/pool/poolbasechannel.py +++ b/src/sardana/pool/poolbasechannel.py @@ -30,7 +30,7 @@ __docformat__ = 'restructuredtext' -from sardana.sardanadefs import AttrQuality +from sardana.sardanadefs import AttrQuality, ElementType from sardana.sardanaattribute import SardanaAttribute from sardana.sardanabuffer import SardanaBuffer from sardana.pool.poolelement import PoolElement @@ -120,6 +120,7 @@ def __init__(self, **kwargs): acq_name = "%s.Acquisition" % self._name self.set_action_cache(self.AcquisitionClass(self, name=acq_name)) self._integration_time = 0 + self._shape = None def has_pseudo_elements(self): """Informs whether this channel forms part of any pseudo element @@ -645,6 +646,51 @@ def set_integration_time(self, integration_time, propagate=1): integration_time = property(get_integration_time, set_integration_time, doc="channel integration time") + # ------------------------------------------------------------------------- + # shape + # ------------------------------------------------------------------------- + + def get_shape(self, cache=True, propagate=1): + if not cache or self._shape is None: + shape = self.read_shape() + self._set_shape(shape, propagate=propagate) + return self._shape + + def _set_shape(self, shape, propagate=1): + self._shape = shape + if not propagate: + return + self.fire_event( + EventType("shape", priority=propagate), shape) + + def read_shape(self): + try: + shape = self.controller.get_axis_par(self.axis, "shape") + except: + shape = None + if shape is None: + # backwards compatibility for controllers not implementing + # shape axis par + if self.get_type() in (ElementType.OneDExpChannel, + ElementType.TwoDExpChannel): + self.warning( + "not implementing shape axis parameter in 1D and 2D " + "controllers is deprecated since Jan21") + value = self.value.value + import numpy + try: + shape = numpy.shape(value) + except Exception as e: + raise RuntimeError( + "can not provide backwards compatibility, you must" + "implement shape axis parameter") from e + # scalar channel + else: + shape = [] + return shape + + shape = property(get_shape, doc="channel value shape") + def _prepare(self): # TODO: think of implementing the preparation in the software # acquisition action, similarly as it is done for the global diff --git a/src/sardana/tango/pool/PoolDevice.py b/src/sardana/tango/pool/PoolDevice.py index 13060ffd4f..246ea19141 100644 --- a/src/sardana/tango/pool/PoolDevice.py +++ b/src/sardana/tango/pool/PoolDevice.py @@ -902,7 +902,7 @@ def _encode_value_ref_chunk(self, value_ref_chunk): def initialize_dynamic_attributes(self): attrs = PoolElementDevice.initialize_dynamic_attributes(self) - non_detect_evts = "integrationtime", + non_detect_evts = "integrationtime", "shape" for attr_name in non_detect_evts: if attr_name in attrs: @@ -941,6 +941,13 @@ def write_IntegrationTime(self, attr): :type attr: :class:`~PyTango.Attribute`""" self.element.integration_time = attr.get_write_value() + def read_Shape(self, attr): + """Reads the shape. + + :param attr: tango attribute + :type attr: :class:`~PyTango.Attribute`""" + attr.set_value(self.element.shape) + class PoolExpChannelDeviceClass(PoolElementDeviceClass): @@ -955,7 +962,14 @@ class PoolExpChannelDeviceClass(PoolElementDeviceClass): attr_list.update(PoolElementDeviceClass.attr_list) standard_attr_list = { - 'ValueBuffer': [[DevEncoded, SCALAR, READ]] + 'ValueBuffer': [[DevEncoded, SCALAR, READ]], + 'Shape': [[DevLong64, SPECTRUM, READ, 2], + {'label': "Shape (X,Y)", + 'description': "Shape of the value. It is an array with \n" + "at most 2 elements: X and Y dimensions. \n" + "0-element array - scalar\n" + "1-element array (X) - spectrum\n" + "2-element array (X, Y) - image"}], } standard_attr_list.update(PoolElementDeviceClass.standard_attr_list) From e01ce34c940c147c231e108c7cd6aafce8bb4b08 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 23 Dec 2020 00:18:50 +0100 Subject: [PATCH 05/82] Implement shape axis par for 1D and 2D dummies --- src/sardana/pool/poolcontrollers/DummyOneDController.py | 9 +++++++++ src/sardana/pool/poolcontrollers/DummyTwoDController.py | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/src/sardana/pool/poolcontrollers/DummyOneDController.py b/src/sardana/pool/poolcontrollers/DummyOneDController.py index 106c3754c6..0009cc67c6 100644 --- a/src/sardana/pool/poolcontrollers/DummyOneDController.py +++ b/src/sardana/pool/poolcontrollers/DummyOneDController.py @@ -317,3 +317,12 @@ def setRoI(self, axis, value): raise ValueError( "RoI[1] exceeds detector dimension ({})".format(dim)) channel.roi = value + + def GetAxisPar(self, axis, par): + idx = axis - 1 + channel = self.channels[idx] + if par == "shape": + roi = channel.roi + if roi == [0, 0]: + return self.BufferSize + return [roi[1] - roi[0]] diff --git a/src/sardana/pool/poolcontrollers/DummyTwoDController.py b/src/sardana/pool/poolcontrollers/DummyTwoDController.py index 9b34c1dc71..f48f63cb4b 100644 --- a/src/sardana/pool/poolcontrollers/DummyTwoDController.py +++ b/src/sardana/pool/poolcontrollers/DummyTwoDController.py @@ -430,6 +430,15 @@ def SetCtrlPar(self, par, value): if par == "synchronization": self._synchronization = value + def GetAxisPar(self, axis, par): + idx = axis - 1 + channel = self.channels[idx] + if par == "shape": + roi = channel.roi + if roi == [0, 0, 0, 0]: + return self.BufferSize + return [roi[1] - roi[0], roi[3] - roi[2]] + def getSynchronizer(self): if self._synchronizer is None: return "None" From 90e6af8d868d122ccffb713f44cf5f6294d1ae11 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 23 Dec 2020 00:19:48 +0100 Subject: [PATCH 06/82] Read shape attribute in scans Instead of using measurement group configuration read shape of experimental channels at the beginning of each scan. --- src/sardana/macroserver/scan/gscan.py | 50 +++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index f0662ad2d1..74d5cb38cb 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -83,6 +83,32 @@ class ScanException(MacroServerException): pass +def _get_shape(channel): + # internal Sardana channel + if isinstance(channel, PyTango.DeviceProxy): + try: + shape = channel.shape + except: + return None + if shape is None: # in PyTango empty spectrum is None + return [] + return shape + # external channel (Tango attribute) + elif isinstance(channel, PyTango.AttributeProxy): + try: + attr_conf = channel.get_config() + except: + return None + if attr_conf.data_format == PyTango.AttrDataFormat.SCALAR: + return [] + try: + value = channel.read().value + except: + return [n for n in (attr_conf.max_dim_x, + attr_conf.max_dim_y) if n > 0] + return list(np.shape(value)) + + class ExtraData(object): def __init__(self, **kwargs): @@ -675,16 +701,24 @@ def _setupEnvironment(self, additional_env): counters = [] for ci in channels_info: full_name = ci.full_name + shape = None + instrument = '' try: # Use DeviceProxy instead of taurus to avoid crashes in Py3 # See: tango-controls/pytango#292 # channel = taurus.Device(full_name) channel = PyTango.DeviceProxy(full_name) - instrument = channel.instrument - except Exception: - # full_name of external channels is the name of the attribute - # external channels are not assigned to instruments - instrument = '' + except: + try: + channel = PyTango.AttributeProxy(full_name) + except: + channel = None + if channel: + shape = _get_shape(channel) + try: + instrument = channel.instrument + except: + instrument = '' try: instrumentFullName = self.macro.findObjs( instrument, type_class=Type.Instrument)[0].getFullName() @@ -692,6 +726,10 @@ def _setupEnvironment(self, additional_env): raise except Exception: instrumentFullName = '' + if shape is None: + self.warning("unknown shape of {}, assuming scalar".format( + ci.name)) + shape = [] # substitute the axis placeholder by the corresponding moveable. plotAxes = [] i = 0 @@ -713,7 +751,7 @@ def _setupEnvironment(self, additional_env): column = ColumnDesc(name=ci.full_name, label=ci.label, dtype=ci.data_type, - shape=ci.shape, + shape=shape, instrument=instrumentFullName, source=ci.source, output=ci.output, From ee812c3e9953da59a3b54768d96546906d85202a Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 23 Dec 2020 00:22:22 +0100 Subject: [PATCH 07/82] flake8 --- src/sardana/macroserver/scan/gscan.py | 12 ++++++------ src/sardana/pool/poolbasechannel.py | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py index 74d5cb38cb..afcad7e31d 100644 --- a/src/sardana/macroserver/scan/gscan.py +++ b/src/sardana/macroserver/scan/gscan.py @@ -88,7 +88,7 @@ def _get_shape(channel): if isinstance(channel, PyTango.DeviceProxy): try: shape = channel.shape - except: + except Exception: return None if shape is None: # in PyTango empty spectrum is None return [] @@ -97,13 +97,13 @@ def _get_shape(channel): elif isinstance(channel, PyTango.AttributeProxy): try: attr_conf = channel.get_config() - except: + except Exception: return None if attr_conf.data_format == PyTango.AttrDataFormat.SCALAR: return [] try: value = channel.read().value - except: + except Exception: return [n for n in (attr_conf.max_dim_x, attr_conf.max_dim_y) if n > 0] return list(np.shape(value)) @@ -708,16 +708,16 @@ def _setupEnvironment(self, additional_env): # See: tango-controls/pytango#292 # channel = taurus.Device(full_name) channel = PyTango.DeviceProxy(full_name) - except: + except Exception: try: channel = PyTango.AttributeProxy(full_name) - except: + except Exception: channel = None if channel: shape = _get_shape(channel) try: instrument = channel.instrument - except: + except Exception: instrument = '' try: instrumentFullName = self.macro.findObjs( diff --git a/src/sardana/pool/poolbasechannel.py b/src/sardana/pool/poolbasechannel.py index a644cbc410..631d5955e6 100644 --- a/src/sardana/pool/poolbasechannel.py +++ b/src/sardana/pool/poolbasechannel.py @@ -666,7 +666,7 @@ def _set_shape(self, shape, propagate=1): def read_shape(self): try: shape = self.controller.get_axis_par(self.axis, "shape") - except: + except Exception: shape = None if shape is None: # backwards compatibility for controllers not implementing From e0e80eb7272f8196a0be9287ce15ad9c1a6b5ae3 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 25 Jan 2021 12:26:07 +0100 Subject: [PATCH 08/82] Fix firing PsuedoCounter shape events Consider event values which are not SardanaAttribute instances as it is in the case of shape axis parameter. --- src/sardana/tango/pool/PseudoCounter.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sardana/tango/pool/PseudoCounter.py b/src/sardana/tango/pool/PseudoCounter.py index f1c422295a..9293d9cf6b 100644 --- a/src/sardana/tango/pool/PseudoCounter.py +++ b/src/sardana/tango/pool/PseudoCounter.py @@ -152,6 +152,8 @@ def _on_pseudo_counter_changed(self, event_source, event_type, else: value = event_value.value timestamp = event_value.timestamp + else: + value = event_value self.set_attribute(attr, value=value, w_value=w_value, timestamp=timestamp, quality=quality, From 9a2ffd4d70ab3235db5735ed3633b6f25acea7c2 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 25 Jan 2021 12:27:14 +0100 Subject: [PATCH 09/82] Do not read shape from the cache Shape may change externally e.g. by changing RoI or binning. Always read it from the controller. --- src/sardana/tango/pool/PoolDevice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/tango/pool/PoolDevice.py b/src/sardana/tango/pool/PoolDevice.py index 246ea19141..5ecd5ee59a 100644 --- a/src/sardana/tango/pool/PoolDevice.py +++ b/src/sardana/tango/pool/PoolDevice.py @@ -946,7 +946,7 @@ def read_Shape(self, attr): :param attr: tango attribute :type attr: :class:`~PyTango.Attribute`""" - attr.set_value(self.element.shape) + attr.set_value(self.element.get_shape(cache=False)) class PoolExpChannelDeviceClass(PoolElementDeviceClass): From 4820387094bc63105eed11e66a79ec27b4f6ca7f Mon Sep 17 00:00:00 2001 From: Tim Schoof Date: Mon, 25 Jan 2021 14:24:09 +0100 Subject: [PATCH 10/82] Fix example in docstring of QtSpockWidget --- src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py index 54d8535170..a20f3d2800 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py @@ -146,8 +146,9 @@ class QtSpockWidget(RichJupyterWidget, TaurusBaseWidget): from taurus.external.qt import Qt from sardana.taurus.qt.qtgui.extra_sardana.qtspock import QtSpockWidget app = Qt.QApplication([]) - widget = QtSpockWidget() + widget = QtSpockWidget(use_model_from_profile=True) widget.show() + widget.start_kernel() app.aboutToQuit.connect(widget.shutdown_kernel) app.exec_() """ From fc14cd8fe8ede413e94c6140629a6b9fa460aa1c Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 26 Jan 2021 14:52:27 +0100 Subject: [PATCH 11/82] Add unittest for addCustomData --- .../macroserver/recorders/test/test_h5storage.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/sardana/macroserver/recorders/test/test_h5storage.py b/src/sardana/macroserver/recorders/test/test_h5storage.py index 413f4e4797..657dff0dfa 100644 --- a/src/sardana/macroserver/recorders/test/test_h5storage.py +++ b/src/sardana/macroserver/recorders/test/test_h5storage.py @@ -193,3 +193,17 @@ def tearDown(self): os.remove(self.path) except OSError: pass + + +@pytest.fixture +def recorder(tmpdir): + path = str(tmpdir / "file.h5") + return NXscanH5_FileRecorder(filename=path) + + +@pytest.mark.parametrize("custom_data", ["str_custom_data", 8, True]) +def test_addCustomData(recorder, custom_data): + name = "custom_data_name" + recorder.addCustomData(custom_data, name) + with h5py.File(recorder.filename) as fd: + assert fd["entry"]["custom_data"][name].value == custom_data From db6a12f8cddcbb6013497ee8c910582ffd5be1e9 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 26 Jan 2021 14:59:40 +0100 Subject: [PATCH 12/82] Fix addCustomData with strings --- src/sardana/macroserver/recorders/h5storage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/macroserver/recorders/h5storage.py b/src/sardana/macroserver/recorders/h5storage.py index 2b34d5abe7..55959a22e0 100644 --- a/src/sardana/macroserver/recorders/h5storage.py +++ b/src/sardana/macroserver/recorders/h5storage.py @@ -578,7 +578,7 @@ def _addCustomData(self, value, name, nxpath=None, dtype=None, **kwargs): if numpy.isscalar(value): dtype = numpy.dtype(type(value)).name if numpy.issubdtype(dtype, str): - dtype = 'char' + dtype = NXscanH5_FileRecorder.str_dt if dtype == 'bool': value, dtype = int(value), 'int8' else: From 92390f3ccf8910878fc734671b1428b21c7a7cef Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 26 Jan 2021 15:35:09 +0100 Subject: [PATCH 13/82] Add missing pytest import --- src/sardana/macroserver/recorders/test/test_h5storage.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sardana/macroserver/recorders/test/test_h5storage.py b/src/sardana/macroserver/recorders/test/test_h5storage.py index 657dff0dfa..d6f32cfcaf 100644 --- a/src/sardana/macroserver/recorders/test/test_h5storage.py +++ b/src/sardana/macroserver/recorders/test/test_h5storage.py @@ -31,6 +31,7 @@ import h5py import numpy +import pytest from unittest import TestCase from sardana.macroserver.scan import ColumnDesc From 01c95cd0b4dd1955f086752f080f0f62664d4bcc Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 26 Jan 2021 16:43:10 +0100 Subject: [PATCH 14/82] Fix PreScanSnapshot of strings --- src/sardana/macroserver/recorders/h5storage.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sardana/macroserver/recorders/h5storage.py b/src/sardana/macroserver/recorders/h5storage.py index 2b34d5abe7..1f2b8597b7 100644 --- a/src/sardana/macroserver/recorders/h5storage.py +++ b/src/sardana/macroserver/recorders/h5storage.py @@ -296,6 +296,7 @@ def _createPreScanSnapshot(self, env): self.debug('Pre-scan snapshot of %s will be stored as type %s', dd.name, dtype) elif dd.dtype == 'str': + dtype = NXscanH5_FileRecorder.str_dt dd.dtype = NXscanH5_FileRecorder.str_dt if dtype in self.supported_dtypes: From 1a05485b92ee5269fb1a0c10759b7554c317b3e7 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Wed, 27 Jan 2021 10:51:22 +0100 Subject: [PATCH 15/82] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03f9c93b57..ec90095c6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ This file follows the formats and conventions from [keepachangelog.com] instead of reading only at the end (#1442, #1448) * Avoid problems when defining different, e.g. shape, standard attributes, e.g. pseudo counter's value, in controllers (#1440, #1446) +* PreScanSnapshot of strings (#1486) * Problems with macro id's when `sequencer` executes from _plain text_ files (#1215, #1216) * `sequencer` loading of plain text sequences in spock syntax with macro functions (#1422) * Allow running Spock without Qt bindings (#1462, #1463) From 578e870a0948e9ad96222462c010d2a789546a03 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Wed, 27 Jan 2021 15:19:37 +0100 Subject: [PATCH 16/82] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec90095c6a..193675afff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,7 +31,8 @@ This file follows the formats and conventions from [keepachangelog.com] instead of reading only at the end (#1442, #1448) * Avoid problems when defining different, e.g. shape, standard attributes, e.g. pseudo counter's value, in controllers (#1440, #1446) -* PreScanSnapshot of strings (#1486) +* Storing string values in PreScanSnapshot in NXscanH5_FileRecorder (#1486) +* Storing string values as custom data in NXscanH5_FileRecorder (#1485) * Problems with macro id's when `sequencer` executes from _plain text_ files (#1215, #1216) * `sequencer` loading of plain text sequences in spock syntax with macro functions (#1422) * Allow running Spock without Qt bindings (#1462, #1463) From 037d75df2b1e406e92bb844e9ce43328b6d8afaa Mon Sep 17 00:00:00 2001 From: schooft <42000386+schooft@users.noreply.github.com> Date: Wed, 27 Jan 2021 16:54:34 +0100 Subject: [PATCH 17/82] Add string to QApplication argument list This is a workaround for a bug in some PyQt versions, see issue #1209. Co-authored-by: reszelaz --- src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py index a20f3d2800..a516bfcbae 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py @@ -145,7 +145,7 @@ class QtSpockWidget(RichJupyterWidget, TaurusBaseWidget): from taurus.external.qt import Qt from sardana.taurus.qt.qtgui.extra_sardana.qtspock import QtSpockWidget - app = Qt.QApplication([]) + app = Qt.QApplication(["qtspock"]) widget = QtSpockWidget(use_model_from_profile=True) widget.show() widget.start_kernel() From 23b0b786a5f799b27e47fe1ba1c2d31ff119de48 Mon Sep 17 00:00:00 2001 From: Tim Schoof Date: Wed, 27 Jan 2021 17:30:41 +0100 Subject: [PATCH 18/82] Show info in widget if no kernel was started yet Because the widget is reset when a new kernel is started, the message is visible only if no kernel has been started yet. --- src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py index a516bfcbae..e9a84ba2ed 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/qtspock.py @@ -181,6 +181,8 @@ def __init__( self._door_name = None self._door_alias = None + self.append_stream("Waiting for kernel to start") + self.kernel_manager = SpockKernelManager(kernel_name=kernel) self.kernel_manager.kernel_about_to_launch.connect( self._handle_kernel_lauched) From 4a5efaa489664200ebfe96872a8679bd16fd2311 Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 28 Jan 2021 12:54:01 +0100 Subject: [PATCH 19/82] Add script to upgrade mntgrp from Sardana 2 to Sardana 3 --- scripts/upgrade/upgrade_mntgrp.py | 106 ++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 scripts/upgrade/upgrade_mntgrp.py diff --git a/scripts/upgrade/upgrade_mntgrp.py b/scripts/upgrade/upgrade_mntgrp.py new file mode 100644 index 0000000000..1154520e03 --- /dev/null +++ b/scripts/upgrade/upgrade_mntgrp.py @@ -0,0 +1,106 @@ +""" This serves to upgrade MeasurementGroups from Sardana 2 to Sardana 3: + +To get usage help: python3 upgrade_mntgrp.py --help +""" + +import re +import sys +try: + import argparse +except ImportError: + from taurus.external import argparse +try: + from itertools import zip_longest +except ImportError: + from itertools import izip_longest as zip_longest + +import tango +import taurus +from sardana.taurus.core.tango.sardana import registerExtensions + + +def grouper(iterable, n, fillvalue=None): + """Collect data into fixed-length chunks or blocks""" + # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx" + args = [iter(iterable)] * n + return zip_longest(*args, fillvalue=fillvalue) + + +def replace_tango_db(tango_db_pqdn, tango_db_fqdn, s): + # first step: pqdn -> fqdn + new_s = re.sub(tango_db_pqdn, tango_db_fqdn, s) + # second step: add missing scheme + new_s = re.sub("(? Date: Thu, 28 Jan 2021 13:33:44 +0100 Subject: [PATCH 20/82] Document upgrade_mntgrp.py script in the migration guide --- doc/source/devel/guide_migration/2to3.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/source/devel/guide_migration/2to3.rst b/doc/source/devel/guide_migration/2to3.rst index 8e4f3939c3..2788143042 100644 --- a/doc/source/devel/guide_migration/2to3.rst +++ b/doc/source/devel/guide_migration/2to3.rst @@ -45,6 +45,10 @@ Sardana v3 was reduced by long time ago deprecated features. You can find a list of them together with the suggested substitutes in this `table `_. +For migrating the measurement group configurations (non-URI model names) you +can use the `upgrade_mntgrp.py `_ +script. + From 30db3e3f172527e95c97dcb46004d8af4c348a20 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Thu, 28 Jan 2021 13:54:03 +0100 Subject: [PATCH 21/82] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 193675afff..f81c78e0f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Problems with macro id's when `sequencer` executes from _plain text_ files (#1215, #1216) * `sequencer` loading of plain text sequences in spock syntax with macro functions (#1422) * Allow running Spock without Qt bindings (#1462, #1463) +* Docstring of QtSpockWidget (#1484) * Recorders tests helpers (#1439) * Disable flake8 job in travis CI (#1455) * `createMacro()` and `prepareMacro()` docstring (#1460, #1444) From e19c58f4d4b72a6d6976e9bfee2e036aacc1f7b6 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 29 Jan 2021 16:44:50 +0100 Subject: [PATCH 22/82] Remove shape from the expconf It was decided in #1296 that shape will be changed from the measurement group configuration to the experimental channel read only attribute. Hence remove it from the expconf widget. --- .../users/taurus/experimentconfiguration.rst | 1 - src/sardana/pool/poolmeasurementgroup.py | 2 +- .../taurus/core/tango/sardana/sardana.py | 2 +- .../qtgui/extra_sardana/measurementgroup.py | 39 +++---------------- 4 files changed, 7 insertions(+), 37 deletions(-) diff --git a/doc/source/users/taurus/experimentconfiguration.rst b/doc/source/users/taurus/experimentconfiguration.rst index 25fe0a7308..0c82b245a7 100644 --- a/doc/source/users/taurus/experimentconfiguration.rst +++ b/doc/source/users/taurus/experimentconfiguration.rst @@ -75,7 +75,6 @@ a given channel or its controller: process. * output - whether the channel acquisition results should be printed, for example, by the output recorder during the scan. Can be either True or False. -* shape - shape of the data * data type - type of the data * plot type - select the online scan plot type for the channel. Can have one of the following values: diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py index 6776a6f045..eb05b13034 100644 --- a/src/sardana/pool/poolmeasurementgroup.py +++ b/src/sardana/pool/poolmeasurementgroup.py @@ -953,7 +953,7 @@ def _fill_channel_data(self, channel, channel_data): channel_data['normalization'] = channel_data.get('normalization', Normalization.No) # TODO: think of filling other keys: data_type, data_units, nexus_path - # shape here instead of feeling them on the Taurus extension level + # here instead of feeling them on the Taurus extension level if ctype != ElementType.External: ctrl_name = channel.controller.full_name diff --git a/src/sardana/taurus/core/tango/sardana/sardana.py b/src/sardana/taurus/core/tango/sardana/sardana.py index 1b0fc8c9b2..f14f94f80c 100644 --- a/src/sardana/taurus/core/tango/sardana/sardana.py +++ b/src/sardana/taurus/core/tango/sardana/sardana.py @@ -56,7 +56,7 @@ "PlotAxes", "Timer", "Monitor", "Synchronization", "ValueRefPattern", "ValueRefEnabled", "Conditioning", "Normalization", "NXPath", - "Shape", "DataType", "Unknown", "Synchronizer")) + "DataType", "Unknown", "Synchronizer")) PlotType = Enumeration("PlotType", ("No", "Spectrum", "Image")) diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py b/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py index e7c16f7e01..3d6171800e 100644 --- a/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py +++ b/src/sardana/taurus/qt/qtgui/extra_sardana/measurementgroup.py @@ -136,7 +136,7 @@ def createChannelDict(channel, index=None, **kwargs): # If the channel is a Tango one, try to guess data_type, shape and # data_units attrproxy = attrconf = value = None - dtype = shape = None + dtype = None try: attrproxy = PyTango.AttributeProxy(source) attrconf = attrproxy.get_config() @@ -147,15 +147,9 @@ def createChannelDict(channel, index=None, **kwargs): print(str(e)) if value is not None: - shape = list(numpy.shape(value)) dtype = getattr(value, 'dtype', numpy.dtype(type(value))).name ret['data_units'] = attrconf.unit elif attrconf is not None: - if attrconf.data_format == PyTango.AttrDataFormat.SCALAR: - shape = [] - else: - shape = [n for n in (attrconf.max_dim_x, - attrconf.max_dim_y) if n > 0] dtype = FROM_TANGO_TO_STR_TYPE[attrconf.data_type] ret['data_units'] = attrconf.unit @@ -166,8 +160,6 @@ def createChannelDict(channel, index=None, **kwargs): # elif dtype == 'bool': # dtype='int8' ret['data_type'] = dtype - if shape is not None: - ret['shape'] = shape # now overwrite using the arguments ret.update(kwargs) @@ -179,13 +171,7 @@ def createChannelDict(channel, index=None, **kwargs): # Choose a default plot_type for the channel if 'plot_type' not in ret: - default_plot_type = {0: PlotType.Spectrum, - 2: PlotType.Image, None: PlotType.No} - try: - rank = len(ret['shape']) - except KeyError: - rank = None # if shape is not known, use the default plot_type - ret['plot_type'] = default_plot_type.get(rank, PlotType.No) + ret['plot_type'] = PlotType.No # And a default value for plot_axes if 'plot_axes' not in ret: @@ -248,8 +234,6 @@ def getElementTypeToolTip(t): return "Channel active or not" elif t == ChannelView.Output: return "Channel output active or not" - elif t == ChannelView.Shape: - return "Shape of the data (using numpy convention). For example, a scalar will have shape=(), a spectrum of 10 elements will have shape=(10,) and an image of 20x30 will be shape=(20,30)" elif t == ChannelView.DataType: return "Type of data for storing (valid types are: char, float32, float64, [u]int{8|16|32|64})", elif t == ChannelView.PlotType: @@ -305,7 +289,6 @@ class MntGrpChannelItem(BaseMntGrpChannelItem): itemdata_keys_map = {ChannelView.Channel: 'label', ChannelView.Enabled: 'enabled', ChannelView.Output: 'output', - ChannelView.Shape: 'shape', ChannelView.DataType: 'data_type', ChannelView.PlotType: 'plot_type', ChannelView.PlotAxes: 'plot_axes', @@ -334,8 +317,6 @@ def data(self, index): ret = Normalization[ret] elif taurus_role == ChannelView.PlotAxes: ret = "|".join(ret) - elif taurus_role == ChannelView.Shape: - ret = str(ret) return ret def setData(self, index, qvalue): @@ -363,16 +344,6 @@ def setData(self, index, qvalue): data = Normalization[qvalue] elif taurus_role == ChannelView.PlotAxes: data = [a for a in qvalue.split('|')] - elif taurus_role == ChannelView.Shape: - s = qvalue - try: - data = eval(s, {}, {}) - if not isinstance(data, (tuple, list)): - raise ValueError - except: - from taurus.core.util.log import Logger - Logger(self.__class__.__name__).error('Invalid shape %s', s) - data = () else: raise NotImplementedError('Unknown role') ch_data[key] = data @@ -394,13 +365,13 @@ class MntGrpUnitItem(TaurusBaseTreeItem): class BaseMntGrpChannelModel(TaurusBaseModel): - ColumnNames = ("Channel", "enabled", "output", "Shape", "Data Type", + ColumnNames = ("Channel", "enabled", "output", "Data Type", "Plot Type", "Plot Axes", "Timer", "Monitor", "Synchronizer", "Synchronization", "Ref Enabled", "Ref Pattern", "Conditioning", "Normalization", "NeXus Path") ColumnRoles = ((ChannelView.Channel, ChannelView.Channel), - ChannelView.Enabled, ChannelView.Output, ChannelView.Shape, + ChannelView.Enabled, ChannelView.Output, ChannelView.DataType, ChannelView.PlotType, ChannelView.PlotAxes, ChannelView.Timer, ChannelView.Monitor, ChannelView.Synchronizer, @@ -978,7 +949,7 @@ class MntGrpChannelEditor(TaurusBaseTableWidget): DftPerspective = "Channel" _simpleViewColumns = (ChannelView.Channel, ChannelView.Output, - ChannelView.Shape, ChannelView.PlotType, ChannelView.PlotAxes) + ChannelView.PlotType, ChannelView.PlotAxes) _simpleView = False def __init__(self, parent=None, designMode=False, with_filter_widget=True, perspective=None): From 3b3ac751a70f98caa51708905b199cc6c6ac67dc Mon Sep 17 00:00:00 2001 From: Benjamin Bertrand Date: Sun, 31 Jan 2021 10:46:27 +0100 Subject: [PATCH 23/82] Add LICENSE to python package --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index 6f5eef71c1..8ca9272ff2 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -12,3 +12,4 @@ exclude .ropeproject exclude build_* include CHANGELOG.md +include LICENSE From b5c9b537591879175977a4f54aa3ff9f3ff4be10 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 1 Feb 2021 09:58:13 +0100 Subject: [PATCH 24/82] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f81c78e0f4..3b6eaccc33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Documentation on how to start Tango servers on fixed IP - ORBendPoint (#1470) * Documentation example on how to more efficiently access Tango with PyTango in macros/controllers (#1456) +* LICENSE file to python source distribution (#1490) ### Fixed From 0ab58a7e0ec385d1f10b8d85437c0c7fe4fce3cd Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 1 Feb 2021 12:52:06 +0100 Subject: [PATCH 25/82] Remove TODO --- .../howto_controllers/howto_countertimercontroller.rst | 6 ------ 1 file changed, 6 deletions(-) diff --git a/doc/source/devel/howto_controllers/howto_countertimercontroller.rst b/doc/source/devel/howto_controllers/howto_countertimercontroller.rst index 6d4f751265..f056e0dbfb 100644 --- a/doc/source/devel/howto_controllers/howto_countertimercontroller.rst +++ b/doc/source/devel/howto_controllers/howto_countertimercontroller.rst @@ -6,12 +6,6 @@ How to write a counter/timer controller ======================================= -.. important:: - Counter/timer controller :term:`API` was extended in SEP18_ but this is - still not documented in this chapter. Please check the said SEP for more - information about the additional :term:`API` or eventual changes. - - The basics ---------- From ac6f0a3213ffc51029c31f0c733e62c8db301ddf Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 1 Feb 2021 13:13:19 +0100 Subject: [PATCH 26/82] Use equality instead of identity checks for numbers and strings Starting with Python 3.8 these are reported as warnings: The compiler now produces a SyntaxWarning when identity checks (is and is not) are used with certain types of literals (e.g. strings, numbers). These can often work by accident in CPython, but are not guaranteed by the language spec. The warning advises users to use equality tests (== and !=) instead. --- src/sardana/macroserver/macros/lists.py | 2 +- src/sardana/tango/core/util.py | 2 +- src/sardana/taurus/core/tango/sardana/macroserver.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sardana/macroserver/macros/lists.py b/src/sardana/macroserver/macros/lists.py index e137453bff..33ae623299 100644 --- a/src/sardana/macroserver/macros/lists.py +++ b/src/sardana/macroserver/macros/lists.py @@ -143,7 +143,7 @@ def obj2Row(self, o, cols=None): def run(self, filter): objs = self.objs(filter) nb = len(objs) - if nb is 0: + if nb == 0: if self.subtype is Macro.All: if isinstance(self.type, str): t = self.type.lower() diff --git a/src/sardana/tango/core/util.py b/src/sardana/tango/core/util.py index 7f84681d03..293aea9a8e 100644 --- a/src/sardana/tango/core/util.py +++ b/src/sardana/tango/core/util.py @@ -1206,7 +1206,7 @@ def prepare_logging(options, args, tango_args, start_time=None, def prepare_rconsole(options, args, tango_args): port = options.rconsole_port - if port is None or port is 0: + if port is None or port == 0: return taurus.debug("Setting up rconsole on port %d...", port) try: diff --git a/src/sardana/taurus/core/tango/sardana/macroserver.py b/src/sardana/taurus/core/tango/sardana/macroserver.py index 8849c9ef81..4518b88d60 100644 --- a/src/sardana/taurus/core/tango/sardana/macroserver.py +++ b/src/sardana/taurus/core/tango/sardana/macroserver.py @@ -665,7 +665,7 @@ def _processInput(self, input_data): input_type = input_data['type'] if input_type == 'input': result = self._input_handler.input(input_data) - if result['input'] is '' and 'default_value' in input_data: + if result['input'] == '' and 'default_value' in input_data: result['input'] = input_data['default_value'] result = CodecFactory().encode('json', ('', result))[1] self.write_attribute('Input', result) From 36975bcab51dc4b875a40e8dbf3d7a8948f97635 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 1 Feb 2021 13:27:06 +0100 Subject: [PATCH 27/82] Add TOC to how to counter/timer controller --- .../howto_controllers/howto_countertimercontroller.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/source/devel/howto_controllers/howto_countertimercontroller.rst b/doc/source/devel/howto_controllers/howto_countertimercontroller.rst index f056e0dbfb..88f36f5083 100644 --- a/doc/source/devel/howto_controllers/howto_countertimercontroller.rst +++ b/doc/source/devel/howto_controllers/howto_countertimercontroller.rst @@ -6,6 +6,13 @@ How to write a counter/timer controller ======================================= +This chapter provides the necessary information to write a counter/timer +controller in Sardana. + +.. contents:: Table of contents + :depth: 3 + :backlinks: entry + The basics ---------- From 4092d3b5ddc6d7a6509b1e468579c2163ce70ebb Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 1 Feb 2021 14:27:57 +0100 Subject: [PATCH 28/82] Move timer/monitor roles to advanced topics --- .../howto_controllers/howto_countertimercontroller.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/source/devel/howto_controllers/howto_countertimercontroller.rst b/doc/source/devel/howto_controllers/howto_countertimercontroller.rst index 88f36f5083..b82f108ff5 100644 --- a/doc/source/devel/howto_controllers/howto_countertimercontroller.rst +++ b/doc/source/devel/howto_controllers/howto_countertimercontroller.rst @@ -217,10 +217,14 @@ Here is an example of the possible implementation of def AbortOne(self, axis): self.springfield.AbortChannel(axis) + +Advanced topics +--------------- + .. _sardana-countertimercontroller-howto-timermonitor: Timer and monitor roles ------------------------ +~~~~~~~~~~~~~~~~~~~~~~~ Usually counters can work in either of two modes: timer or monitor. In both of them, one counter in a group is assigned a special role to control when @@ -237,9 +241,6 @@ Controller may announce its default timer axis with the .. _sardana-countertimercontroller-howto-advanced: -Advanced topics ---------------- - .. _sardana-countertimercontroller-howto-timestamp-value: Timestamp a counter value From 64292674584beb3d97b0be1b518ff0dfd641536d Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 1 Feb 2021 14:28:38 +0100 Subject: [PATCH 29/82] Document hardware and software start synchronization --- .../howto_countertimercontroller.rst | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/doc/source/devel/howto_controllers/howto_countertimercontroller.rst b/doc/source/devel/howto_controllers/howto_countertimercontroller.rst index b82f108ff5..f7c88d918a 100644 --- a/doc/source/devel/howto_controllers/howto_countertimercontroller.rst +++ b/doc/source/devel/howto_controllers/howto_countertimercontroller.rst @@ -410,8 +410,8 @@ We can modify our counter controller to take profit of this hardware feature: def StartAll(self): self.springfield.startCounters(self._counters_info) -Hardware synchronization -~~~~~~~~~~~~~~~~~~~~~~~~ +External (hardware) synchronization +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The synchronization achieved in :ref:`sardana-countertimercontroller-howto-mutliple-acquisition` may not be enough when it comes to acquiring with multiple controllers at the @@ -448,8 +448,10 @@ Here is an example of the possible implementation of SynchMap = { AcqSynch.SoftwareTrigger : 1, AcqSynch.SoftwareGate : 2, - AcqSynch.HardwareTrigger: 3, - AcqSynch.HardwareGate: 4 + AcqSynch.SoftwareStart : 3, + AcqSynch.HardwareTrigger: 4, + AcqSynch.HardwareGate: 5, + AcqSynch.HardwareStart: 6 } def SetCtrlPar(self, name, value): @@ -503,8 +505,11 @@ can be: :attr:`~sardana.pool.pooldefs.AcqSynch.SoftwareGate` synchronization - a sequence of counter values: either :class:`float` or :obj:`~sardana.sardanavalue.SardanaValue` - in case of the :attr:`~sardana.pool.pooldefs.AcqSynch.HardwareTrigger` or - :attr:`~sardana.pool.pooldefs.AcqSynch.HardwareGate` synchronization + in case of the :attr:`~sardana.pool.pooldefs.AcqSynch.HardwareTrigger`, + :attr:`~sardana.pool.pooldefs.AcqSynch.HardwareGate`, + :attr:`~sardana.pool.pooldefs.AcqSynch.HardwareStart` or + :attr:`~sardana.pool.pooldefs.AcqSynch.SoftwareStart` + synchronization Sardana assumes that the counter values are returned in the order of acquisition and that there are no gaps in between them. From 05549efd2f7d9aef99d77f38deafd9c8209ec653 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 1 Feb 2021 16:06:15 +0100 Subject: [PATCH 30/82] Document latency time --- .../howto_countertimercontroller.rst | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/doc/source/devel/howto_controllers/howto_countertimercontroller.rst b/doc/source/devel/howto_controllers/howto_countertimercontroller.rst index f7c88d918a..0bd9f92e0c 100644 --- a/doc/source/devel/howto_controllers/howto_countertimercontroller.rst +++ b/doc/source/devel/howto_controllers/howto_countertimercontroller.rst @@ -483,6 +483,60 @@ Here is an example of the possible implementation of self.springfield.SetRepetitions(repetitions) return value +In order to make the acquisition flow smoothly the synchronizer and +the counter/timer controllers needs to agree on the synchronization pace. +The counter/timer controller manifest what is the maximum allowed pace for him +by means of the ``latency_time`` controller parameter (in seconds). This parameter +corresponds to the minimum time necessary by the hardware controller to re-arm +for the next acquisition. + +Here is an example of the possible implementation of +:meth:`~sardana.pool.controller.Controller.GetCtrlPar`: + +.. code-block:: python + :emphasize-lines: 3 + + class SpringfieldCounterTimerController(CounterTimerController): + + def GetCtrlPar(self, name): + if name == "latency_time": + return self.springfield.GetLatencyTime() + +.. warning:: + By default, the `~sardana.pool.controller.CounterTimerController` + base classes returns zero latency time controller parameter. + If in your controller you override + the :meth:`~sardana.pool.controller.Controller.GetCtrlPar` method + remember to always call the super class method as fallback: + + .. code-block:: python + :emphasize-lines: 5 + + def GetCtrlPar(self, name): + if name == "some_par": + return "some_val" + else: + return super().GetCtrlPar(name) + + +In the case of the :attr:`~sardana.pool.pooldefs.AcqSynch.HardwareStart` or +:attr:`~sardana.pool.pooldefs.AcqSynch.SoftwareStart` synchronizations +the counter/timer hardware *auto* triggers itself during the measurement process. +In order to fully configure the hardware and set the re-trigger pace you can +use the ``latency`` argument (in seconds) +of the :meth:`~sardana.pool.controller.Loadable.LoadOne` method: + +.. code-block:: python + :emphasize-lines: 3 + + class SpringfieldCounterTimerController(CounterTimerController): + + def LoadOne(self, axis, value, repetitions, latency): + self.springfield.LoadChannel(axis, value) + self.springfield.SetRepetitions(repetitions) + self.springfield.SetLatency(latency) + return value + Get counter values """""""""""""""""" From ec258579ab643fe88f8f4df6ecb7600c22bbd4ca Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 1 Feb 2021 16:06:37 +0100 Subject: [PATCH 31/82] Document per measurement preparation --- .../howto_countertimercontroller.rst | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/doc/source/devel/howto_controllers/howto_countertimercontroller.rst b/doc/source/devel/howto_controllers/howto_countertimercontroller.rst index 0bd9f92e0c..1fa444fc7d 100644 --- a/doc/source/devel/howto_controllers/howto_countertimercontroller.rst +++ b/doc/source/devel/howto_controllers/howto_countertimercontroller.rst @@ -568,7 +568,31 @@ can be: Sardana assumes that the counter values are returned in the order of acquisition and that there are no gaps in between them. -.. todo:: document how to skip the readouts while acquiring +Per measurement preparation +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Since SEP18_ counter/timer controllers may take a profit from the per measurement +preparation and reserve resources for a sequence of +:attr:`~sardana.pool.pooldefs.AcqSynch.SoftwareTrigger` +or :attr:`~sardana.pool.pooldefs.AcqSynch.SoftwareGate` acquisitions +already in the :meth:`~sardana.pool.controller.Loadable.PrepareOne` method. +This method is called only once at the beginning of the measurement e.g. +:ref:`Deterministic step scans ` +or :ref:`sardana-users-scan-continuous`. +It enables an opportunity for significant dead time optimization thanks to the +single per measurement configuration instead of the multiple per acquisition +preparation using the :meth:`~sardana.pool.controller.Loadable.LoadOne`. + +Here is an example of the possible implementation of +:meth:`~sardana.pool.controller.Loadable.PrepareOne`: + +.. code-block:: python + :emphasize-lines: 3 + + class SpringfieldCounterTimerController(CounterTimerController): + + def PrepareOne(self, value, repetitions, latency, nb_starts): + return self.springfield.SetNbStarts() .. _ALBA: http://www.cells.es/ From 53b8c8dc428c26f6087a00667899b6d4eae7ebcb Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 1 Feb 2021 16:07:02 +0100 Subject: [PATCH 32/82] Add more detail to LoadOne documentation --- .../devel/howto_controllers/howto_countertimercontroller.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/devel/howto_controllers/howto_countertimercontroller.rst b/doc/source/devel/howto_controllers/howto_countertimercontroller.rst index 1fa444fc7d..0ed780c81a 100644 --- a/doc/source/devel/howto_controllers/howto_countertimercontroller.rst +++ b/doc/source/devel/howto_controllers/howto_countertimercontroller.rst @@ -467,7 +467,7 @@ Multiple acquisitions It is a very common scenario to execute multiple hardware synchronized acquisitions in a row. One example of this type of measurements are the :ref:`sardana-users-scan-continuous`. The controller receives the number of -acquisitions via the third argument of the +acquisitions via the ``repetitions`` argument of the :meth:`~sardana.pool.controller.Loadable.LoadOne` method. Here is an example of the possible implementation of From 8ee19847c68a583d6918c4c525041942f1e6cfdb Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 1 Feb 2021 17:06:26 +0100 Subject: [PATCH 33/82] Correct typo --- .../devel/howto_controllers/howto_countertimercontroller.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/devel/howto_controllers/howto_countertimercontroller.rst b/doc/source/devel/howto_controllers/howto_countertimercontroller.rst index 0ed780c81a..2f620616d4 100644 --- a/doc/source/devel/howto_controllers/howto_countertimercontroller.rst +++ b/doc/source/devel/howto_controllers/howto_countertimercontroller.rst @@ -504,7 +504,7 @@ Here is an example of the possible implementation of .. warning:: By default, the `~sardana.pool.controller.CounterTimerController` - base classes returns zero latency time controller parameter. + base classes return zero latency time controller parameter. If in your controller you override the :meth:`~sardana.pool.controller.Controller.GetCtrlPar` method remember to always call the super class method as fallback: From ef4a8e112ec02e0614dd7bf813c983f669c6bf4c Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 1 Feb 2021 17:15:07 +0100 Subject: [PATCH 34/82] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b6eaccc33..88b5808054 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Initial delay in position domain to the synchronization description in *ct* like continuous scans (#1428) * Avoid double printing of user units in PMTV: read widget and units widget (#1424) +* Missing documentation of SEP18 concepts to how-to counter/timer controller (#995, #1492) * Document how to properly deal with exceptions in macros in order to not interfer with macro stopping/aborting (#1461) * Documentation on how to start Tango servers on fixed IP - ORBendPoint (#1470) From d3f76e65d6e4470865a2fbf86329497aa56fbb1b Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 1 Feb 2021 17:16:18 +0100 Subject: [PATCH 35/82] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88b5808054..1315493d49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Problems with macro id's when `sequencer` executes from _plain text_ files (#1215, #1216) * `sequencer` loading of plain text sequences in spock syntax with macro functions (#1422) * Allow running Spock without Qt bindings (#1462, #1463) +* Use equality instead of identity checks for numbers and strings (#1491) * Docstring of QtSpockWidget (#1484) * Recorders tests helpers (#1439) * Disable flake8 job in travis CI (#1455) From 773a02dbe1b06805e2d654f8484fff9c336b1361 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 2 Feb 2021 22:54:24 +0100 Subject: [PATCH 36/82] Add Referable interface to API docs --- doc/source/devel/api/sardana/pool/controller.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/doc/source/devel/api/sardana/pool/controller.rst b/doc/source/devel/api/sardana/pool/controller.rst index 9377423f70..cef9637a52 100644 --- a/doc/source/devel/api/sardana/pool/controller.rst +++ b/doc/source/devel/api/sardana/pool/controller.rst @@ -25,6 +25,7 @@ :columns: 3 * :class:`Readable` + * :class:`Referable` * :class:`Startable` * :class:`Stopable` * :class:`Loadable` @@ -59,6 +60,17 @@ Readable interface :members: :undoc-members: +Referable interface +------------------- + +.. inheritance-diagram:: Referable + :parts: 1 + +.. autoclass:: Referable + :show-inheritance: + :members: + :undoc-members: + Startable interface ------------------- From 287ce150f3cc6e92e7ee5a2a33942dbf5cacea45 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 2 Feb 2021 22:55:13 +0100 Subject: [PATCH 37/82] Add links in how-to counter/timer controller guide --- .../howto_countertimercontroller.rst | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/doc/source/devel/howto_controllers/howto_countertimercontroller.rst b/doc/source/devel/howto_controllers/howto_countertimercontroller.rst index 2f620616d4..ccd319b2af 100644 --- a/doc/source/devel/howto_controllers/howto_countertimercontroller.rst +++ b/doc/source/devel/howto_controllers/howto_countertimercontroller.rst @@ -1,6 +1,6 @@ .. currentmodule:: sardana.pool.controller -.. _sardana-countertimercontroller-howto-basics: +.. _sardana-countertimercontroller: ======================================= How to write a counter/timer controller @@ -13,6 +13,8 @@ controller in Sardana. :depth: 3 :backlinks: entry +.. _sardana-countertimercontroller-howto-basics: + The basics ---------- @@ -217,6 +219,7 @@ Here is an example of the possible implementation of def AbortOne(self, axis): self.springfield.AbortChannel(axis) +.. _sardana-countertimercontroller-howto-advanced: Advanced topics --------------- @@ -239,8 +242,6 @@ parameter ``acquisition_mode``. Controller may announce its default timer axis with the :obj:`~sardana.pool.controller.Loadable.default_timer` class attribute. -.. _sardana-countertimercontroller-howto-advanced: - .. _sardana-countertimercontroller-howto-timestamp-value: Timestamp a counter value @@ -410,6 +411,8 @@ We can modify our counter controller to take profit of this hardware feature: def StartAll(self): self.springfield.startCounters(self._counters_info) +.. _sardana-countertimercontroller-howto-external-synchronization: + External (hardware) synchronization ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -537,6 +540,8 @@ of the :meth:`~sardana.pool.controller.Loadable.LoadOne` method: self.springfield.SetLatency(latency) return value +.. _sardana-countertimercontroller-howto-external-synchronization-get-values: + Get counter values """""""""""""""""" @@ -568,6 +573,8 @@ can be: Sardana assumes that the counter values are returned in the order of acquisition and that there are no gaps in between them. +.. _sardana-countertimercontroller-per-measurement-preparation: + Per measurement preparation ~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 48305151ebfe3dea57bd2e24d7158bf608302af8 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 2 Feb 2021 22:55:40 +0100 Subject: [PATCH 38/82] Document how-to 2D controller guide --- .../howto_controllers/howto_2dcontroller.rst | 188 +++++++++++++++++- 1 file changed, 183 insertions(+), 5 deletions(-) diff --git a/doc/source/devel/howto_controllers/howto_2dcontroller.rst b/doc/source/devel/howto_controllers/howto_2dcontroller.rst index bc73fceb67..daec570c5c 100644 --- a/doc/source/devel/howto_controllers/howto_2dcontroller.rst +++ b/doc/source/devel/howto_controllers/howto_2dcontroller.rst @@ -1,16 +1,192 @@ .. currentmodule:: sardana.pool.controller -.. _sardana-2dcontroller-howto-basics: +.. _sardana-2dcontroller-howto: ============================ How to write a 2D controller ============================ -The basics ----------- +This chapter provides the necessary information to write a two dimensional (2D) +experimental channel controller in Sardana. + +.. contents:: Table of contents + :depth: 3 + :backlinks: entry + +.. _sardana-2dcontroller-general-guide: + +General guide +------------- + +:ref:`2D experimental channels ` +together with :ref:`1D experimental channels ` +and :ref:`counter/timers ` +belong to the same family of *timerable* experimental channels. + +To write a 2D controller class you can follow +the :ref:`sardana-countertimercontroller` guide keeping in mind +differences explained in continuation. + +.. _sardana-2dcontroller-differences-countertimer: + +Differences with counter/timer controller +----------------------------------------- + +Class definition +~~~~~~~~~~~~~~~~ + +:ref:`The basics of the counter/timer controller ` +chapter explains how to define the counter/timer controller class. +Here you need to simply inherit from the +`~sardana.pool.controller.TwoDController` class: + +.. code-block:: python + :emphasize-lines: 3 + + from sardana.pool.controller import TwoDController + + class SpringfieldTwoDController(TwoDController): + + def __init__(self, inst, props, *args, **kwargs): + super().__init__(inst, props, *args, **kwargs) + +.. _sardana-2dcontroller-getvalue: + +Get 2D value +~~~~~~~~~~~~ + +:ref:`Get counter value ` chapter +explains how to read a counter/timer value +using the :meth:`~sardana.pool.controller.Readable.ReadOne` method. +Here you need to implement the same method but its return value +must be a two-dimensional `numpy.array` (or eventually +a `~sardana.sardanavalue.SardanaValue` object) containing an image instead +of a scalar value. + +.. _sardana-2dcontroller-getvalues: + +Get 2D values +~~~~~~~~~~~~~ + +:ref:`Get counter values ` +chapter explains how to read counter/timer values +using the :meth:`~sardana.pool.controller.Readable.ReadOne` method while +acquiring with external (hardware) synchronization. +Here you need to implement the same method but its return value +must be a sequence with two-dimensional `numpy.array` objects (or eventually +with :obj:`~sardana.sardanavalue.SardanaValue` objects) containing the images +instead of a scalar values. + +Advanced topics +--------------- + +.. _sardana-2dcontroller-valuereferencing: + +Working with value referencing +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +2D experimental channels may produce big arrays of data at high +frame rate. Reading this data and storing it using sardana +is not always optimal. `SEP2`_ introduced data saving duality, optionally, +leaving the data storage at the responsibility of the detector +(or an intermediate software layer e.g. `LImA`_). In this case sardana +just deals with the reference to the data. + +In order to announce the referencing capability the 2D controller must +additionally inherit from the `~sardana.pool.controller.Referable` class: + +.. code-block:: python + :emphasize-lines: 3 + + from sardana.pool.controller import TwoDController, Referable + + class SpringfieldTwoDController(TwoDController, Referable): + + def __init__(self, inst, props, *args, **kwargs): + super().__init__(inst, props, *args, **kwargs) + +.. _sardana-2dcontroller-getvaluereference: + +Get 2D value reference +"""""""""""""""""""""" + +To get the 2D value reference, sardana calls the +:meth:`~sardana.pool.controller.Referable.RefOne` method. This method +receives an axis as parameter and should return a URI (`str`) +pointing to the value. + +Here is an example of the possible implementation of +:meth:`~sardana.pool.controller.Referable.RefOne`: + +.. code-block:: python + :emphasize-lines: 3 + + class SpringfieldTwoDController(TwoDController): + + def RefOne(self, axis): + value_ref = self.springfield.getValueRef(axis) + return value_ref + +.. _sardana-2dcontroller-getvaluesreferences: + +Get 2D values references +"""""""""""""""""""""""" + +:ref:`Get counter values ` +chapter explains how to read counter/timer values +using the :meth:`~sardana.pool.controller.Readable.ReadOne` method while +acquiring with external (hardware) synchronization. +Here you need to implement the :meth:`~sardana.pool.controller.Referable.RefOne` +method and its return value must be a sequence with URIs (`str`) +pointing to the values. + +.. _sardana-2dcontroller-configvaluereference: + +Configure 2D value reference +"""""""""""""""""""""""""""" + +Two axis parameters: ``value_ref_pattern`` (`str`) +and ``value_ref_enabled`` (`bool`) are foreseen for configuring where to store +the values and whether to use the value referencing. Here you need to implement +the :meth:`~sardana.pool.controller.Controller.SetAxiPar` method. + +Here is an example of the possible implementation of +:meth:`~sardana.pool.controller.Controller.SetAxisPar`: + +.. code-block:: python + + class SpringfieldTwoDController(TwoDController): + + def SetAxisPar(self, axis, par, value): + if par == "value_ref_pattern": + self.springfield.setValueRefPattern(axis, value) + elif par == "value_ref_enabled": + self.springfield.setValueRefEnabled(axis, value) + +.. hint:: + Use `Python Format String Syntax `_ + e.g. ``file:///tmp/sample1_{index:02d}`` to configure a dynamic value + referencing using the acquisition index or any other parameter + (acquisition index can be reset in the + :ref:`per measurement preparation `. + phase) + +When value referencing is used +"""""""""""""""""""""""""""""" + +Sardana will :ref:`sardana-2dcontroller-getvaluereference` when: + + - channel has referencing capability and it is enabled + +Sardana will :ref:`sardana-2dcontroller-getvalue` when any of these +conditions applies: + + - channel does not have referencing capability + - channel has referencing capability but it is disabled + - there is a pseudo counter based on this channel + +Hence, in some configurations, both methods may be used simultaneously. -.. todo:: document 2D controller howto - .. _ALBA: http://www.cells.es/ .. _ANKA: http://http://ankaweb.fzk.de/ .. _ELETTRA: http://http://www.elettra.trieste.it/ @@ -35,3 +211,5 @@ The basics .. _numpy: http://numpy.scipy.org/ .. _SPEC: http://www.certif.com/ .. _EPICS: http://www.aps.anl.gov/epics/ +.. _SEP2: http://www.sardana-controls.org/sep/?SEP2.md +.. _LImA: https://lima1.readthedocs.io/en/latest/ From 07204ff1d8b50900af4e57e91f82881db47a55e1 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 2 Feb 2021 22:55:49 +0100 Subject: [PATCH 39/82] Document how-to 1D controller guide --- .../howto_controllers/howto_1dcontroller.rst | 94 ++++++++++++++++++- 1 file changed, 92 insertions(+), 2 deletions(-) diff --git a/doc/source/devel/howto_controllers/howto_1dcontroller.rst b/doc/source/devel/howto_controllers/howto_1dcontroller.rst index 634654e76f..3ece6b1888 100644 --- a/doc/source/devel/howto_controllers/howto_1dcontroller.rst +++ b/doc/source/devel/howto_controllers/howto_1dcontroller.rst @@ -9,8 +9,96 @@ How to write a 1D controller The basics ---------- -.. todo:: document 1D controller howto - +This chapter provides the necessary information to write a one dimensional (1D) +experimental channel controller in Sardana. + +.. contents:: Table of contents + :depth: 3 + :backlinks: entry + +.. _sardana-1dcontroller-general-guide: + +General guide +------------- + +:ref:`1D experimental channels ` +together with :ref:`2D experimental channels ` +and :ref:`counter/timers ` +belong to the same family of *timerable* experimental channels. + +To write a 1D controller class you can follow +the :ref:`sardana-countertimercontroller` guide keeping in mind +differences explained in continuation. + +.. _sardana-1dcontroller-differences-countertimer: + +Differences with counter/timer controller +----------------------------------------- + +Class definition +~~~~~~~~~~~~~~~~ + +:ref:`The basics of the counter/timer controller ` +chapter explains how to define the counter/timer controller class. +Here you need to simply inherit from the +`~sardana.pool.controller.OneDController` class: + +.. code-block:: python + :emphasize-lines: 3 + + from sardana.pool.controller import OneDController + + class SpringfieldOneDController(OneDController): + + def __init__(self, inst, props, *args, **kwargs): + super().__init__(inst, props, *args, **kwargs) + +.. _sardana-1dcontroller-getvalue: + +Get 1D value +~~~~~~~~~~~~ + +:ref:`Get counter value ` chapter +explains how to read a counter/timer value +using the :meth:`~sardana.pool.controller.Readable.ReadOne` method. +Here you need to implement the same method but its return value +must be a one-dimensional `numpy.array` (or eventually +a `~sardana.sardanavalue.SardanaValue` object) containing the spectrum instead +of a scalar value. + +.. _sardana-1dcontroller-getvalues: + +Get 1D values +~~~~~~~~~~~~~ + +:ref:`Get counter values ` +chapter explains how to read counter/timer values +using the :meth:`~sardana.pool.controller.Readable.ReadOne` method while +acquiring with external (hardware) synchronization. +Here you need to implement the same method but its return value +must be a sequence with one-dimensional `numpy.array` objects (or eventually +with :obj:`~sardana.sardanavalue.SardanaValue` objects) containing spectrums +instead of scalar values. + +Advanced topics +--------------- + +.. _sardana-1dcontroller-valuereferencing: + +Working with value referencing +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +1D experimental channels may produce significant arrays of data at high +frame rate. Reading this data and storing it using sardana +is not always optimal. `SEP2`_ introduced data saving duality, optionally, +leaving the data storage at the responsibility of the detector +(or an intermediate software layer e.g. `LImA`_). In this case sardana +just deals with the reference to the data. + +Please refer to :ref:`sardana-2dcontroller-valuereferencing` chapter from +:ref:`sardana-2dcontroller-howto` in order to implement this feature for 1D +controller. + .. _ALBA: http://www.cells.es/ .. _ANKA: http://http://ankaweb.fzk.de/ .. _ELETTRA: http://http://www.elettra.trieste.it/ @@ -35,3 +123,5 @@ The basics .. _numpy: http://numpy.scipy.org/ .. _SPEC: http://www.certif.com/ .. _EPICS: http://www.aps.anl.gov/epics/ +.. _SEP2: http://www.sardana-controls.org/sep/?SEP2.md +.. _LImA: https://lima1.readthedocs.io/en/latest/ From 26b3e1db98ce0280e612b5908478147b7050dd5c Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 3 Feb 2021 22:55:06 +0100 Subject: [PATCH 40/82] Rename ROI to RoI --- doc/source/glossary.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/glossary.rst b/doc/source/glossary.rst index 3e34bf868b..186604e130 100644 --- a/doc/source/glossary.rst +++ b/doc/source/glossary.rst @@ -430,8 +430,8 @@ Glossary dial See :term:`dial position` - ROI - *Region of interest* are samples within a data set identified for a + RoI + *Region of Interest* are samples within a data set identified for a particular purpose. .. _plug-in: http://en.wikipedia.org/wiki/Plug-in_(computing) From ff0f2eca4f73672db7ccc8fb9f9fcde6cf42fe99 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 3 Feb 2021 22:55:44 +0100 Subject: [PATCH 41/82] Add how-to get shape of 2D --- .../howto_controllers/howto_2dcontroller.rst | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/doc/source/devel/howto_controllers/howto_2dcontroller.rst b/doc/source/devel/howto_controllers/howto_2dcontroller.rst index daec570c5c..7e52c94283 100644 --- a/doc/source/devel/howto_controllers/howto_2dcontroller.rst +++ b/doc/source/devel/howto_controllers/howto_2dcontroller.rst @@ -27,6 +27,31 @@ To write a 2D controller class you can follow the :ref:`sardana-countertimercontroller` guide keeping in mind differences explained in continuation. +.. _sardana-2dcontroller-general-guide-shape: + +Get 2D shape +~~~~~~~~~~~~ + +2D controller must provide a shape of the image which will be produced by +acquisition. The shape can be either static e.g. defined by the detector's +sensor size or dynamic e.g. depending on the detector's (or an intermediate +control software layer e.g. `LImA`_) configuration like :term:`RoI` or binning. + +In any case you must provide the shape in the format of a two-element sequence +with horizonatal and vertical dimensions using +the :meth:`~sardana.pool.controller.Controller.GetAxisPar` method. + +Here is an example of the possible implementation of +:meth:`~sardana.pool.controller.Controller.GetAxisPar`: + +.. code-block:: python + + class SpringfieldTwoDController(TwoDController): + + def GetAxisPar(self, axis, par): + if par == "shape": + return self.springfield.getShape(axis) + .. _sardana-2dcontroller-differences-countertimer: Differences with counter/timer controller From a4b43c754467ab632234419b57ab736d6c3c408e Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 3 Feb 2021 22:56:25 +0100 Subject: [PATCH 42/82] Add how-to get shape of 1D --- .../howto_controllers/howto_1dcontroller.rst | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/doc/source/devel/howto_controllers/howto_1dcontroller.rst b/doc/source/devel/howto_controllers/howto_1dcontroller.rst index 3ece6b1888..bbb4319a7e 100644 --- a/doc/source/devel/howto_controllers/howto_1dcontroller.rst +++ b/doc/source/devel/howto_controllers/howto_1dcontroller.rst @@ -30,6 +30,31 @@ To write a 1D controller class you can follow the :ref:`sardana-countertimercontroller` guide keeping in mind differences explained in continuation. +.. _sardana-1dcontroller-general-guide-shape: + +Get 1D shape +~~~~~~~~~~~~ + +1D controller must provide a shape of the spectrum which will be produced by +acquisition. The shape can be either static e.g. defined by the detector's +sensor size or dynamic e.g. depending on the detector's (or an intermediate +control software layer e.g. `LImA`_) configuration like :term:`RoI` or binning. + +In any case you must provide the shape in the format of a one-element sequence +with the length of the spectrum using +the :meth:`~sardana.pool.controller.Controller.GetAxisPar` method. + +Here is an example of the possible implementation of +:meth:`~sardana.pool.controller.Controller.GetAxisPar`: + +.. code-block:: python + + class SpringfieldOneDController(TwoDController): + + def GetAxisPar(self, axis, par): + if par == "shape": + return self.springfield.getShape(axis) + .. _sardana-1dcontroller-differences-countertimer: Differences with counter/timer controller From bbfaf7feec9bb49ed9655305de1d6ddd4c12fddf Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 3 Feb 2021 22:57:15 +0100 Subject: [PATCH 43/82] Rename shape to format in pseudo counter how-to --- .../howto_controllers/howto_pseudocountercontroller.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/source/devel/howto_controllers/howto_pseudocountercontroller.rst b/doc/source/devel/howto_controllers/howto_pseudocountercontroller.rst index cf08f43da5..232bd0add9 100644 --- a/doc/source/devel/howto_controllers/howto_pseudocountercontroller.rst +++ b/doc/source/devel/howto_controllers/howto_pseudocountercontroller.rst @@ -80,12 +80,12 @@ Pseudo counters instantiated from your controller will have a default interface, which among others, comprises the *value* attribute. This attribute is feed with the result of the :meth:`~sardana.pool.controller.PseudoCounterController.calc` method and by -default it expects values of ``float`` type and scalar shape. You can easily +default it expects values of ``float`` type and scalar format. You can easily :ref:`change the default interface `. -This way you could program a pseudo counter to obtain an image :term:`ROI` +This way you could program a pseudo counter to obtain an image :term:`RoI` of a :ref:`2D experimental channel `. -Here is an example of how to change *value* attribute's shape to an image +Here is an example of how to change *value* attribute's format to an image and specify its maximum dimension of 1024 x 1024 pixels: .. code-block:: python From 43e5cb52aac4d27216fb15d4ebe6c4d7d641f704 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 3 Feb 2021 22:57:35 +0100 Subject: [PATCH 44/82] Add how-to get shape for pseudo counter --- .../howto_pseudocountercontroller.rst | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/doc/source/devel/howto_controllers/howto_pseudocountercontroller.rst b/doc/source/devel/howto_controllers/howto_pseudocountercontroller.rst index 232bd0add9..cb690600a1 100644 --- a/doc/source/devel/howto_controllers/howto_pseudocountercontroller.rst +++ b/doc/source/devel/howto_controllers/howto_pseudocountercontroller.rst @@ -97,6 +97,26 @@ and specify its maximum dimension of 1024 x 1024 pixels: axis_attrs['Value'][MaxDimSize] = (1024, 1024) return axis_attrs +Get pseudo counter shape +------------------------ + +If you change the pseudo counter format to spectrum or image then you +controller must provide the shape of the calculation result in either +of the formats: + +- one-element sequence with the length of the spectrum +- two-element sequence with the horizonatal and vertical dimensions of the image + +using the :meth:`~sardana.pool.controller.Controller.GetAxisPar` method. + +Here is an example of the possible implementation of +:meth:`~sardana.pool.controller.Controller.GetAxisPar`: + +.. code-block:: python + + def GetAxisPar(self, axis, par): + if par == "shape": + return [1024, 1024] Including external variables in the calculation ----------------------------------------------- From 169d47a5bd52f7107675d900fafef64b1ffacf8a Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 3 Feb 2021 22:57:47 +0100 Subject: [PATCH 45/82] Fix indentation --- .../devel/howto_controllers/howto_pseudocountercontroller.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/devel/howto_controllers/howto_pseudocountercontroller.rst b/doc/source/devel/howto_controllers/howto_pseudocountercontroller.rst index cb690600a1..4a8c6b1b3d 100644 --- a/doc/source/devel/howto_controllers/howto_pseudocountercontroller.rst +++ b/doc/source/devel/howto_controllers/howto_pseudocountercontroller.rst @@ -90,7 +90,7 @@ and specify its maximum dimension of 1024 x 1024 pixels: .. code-block:: python - def GetAxisAttributes(self, axis): + def GetAxisAttributes(self, axis): axis_attrs = PseudoCounterController.GetAxisAttributes(self, axis) axis_attrs = dict(axis_attrs) axis_attrs['Value'][Type] = ((float, ), ) From 31f475c0587502e8447a768c955f97a0f8ed3571 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 3 Feb 2021 22:58:35 +0100 Subject: [PATCH 46/82] Fix wrong reference o SetAxisPar --- doc/source/devel/howto_controllers/howto_2dcontroller.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/devel/howto_controllers/howto_2dcontroller.rst b/doc/source/devel/howto_controllers/howto_2dcontroller.rst index 7e52c94283..daf6d97cf1 100644 --- a/doc/source/devel/howto_controllers/howto_2dcontroller.rst +++ b/doc/source/devel/howto_controllers/howto_2dcontroller.rst @@ -173,7 +173,7 @@ Configure 2D value reference Two axis parameters: ``value_ref_pattern`` (`str`) and ``value_ref_enabled`` (`bool`) are foreseen for configuring where to store the values and whether to use the value referencing. Here you need to implement -the :meth:`~sardana.pool.controller.Controller.SetAxiPar` method. +the :meth:`~sardana.pool.controller.Controller.SetAxisPar` method. Here is an example of the possible implementation of :meth:`~sardana.pool.controller.Controller.SetAxisPar`: From f40114d41348d01796e87a906caa0163d396dd39 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 3 Feb 2021 22:58:59 +0100 Subject: [PATCH 47/82] Minor correction --- doc/source/devel/howto_controllers/howto_1dcontroller.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/devel/howto_controllers/howto_1dcontroller.rst b/doc/source/devel/howto_controllers/howto_1dcontroller.rst index bbb4319a7e..429ac416d6 100644 --- a/doc/source/devel/howto_controllers/howto_1dcontroller.rst +++ b/doc/source/devel/howto_controllers/howto_1dcontroller.rst @@ -121,7 +121,7 @@ leaving the data storage at the responsibility of the detector just deals with the reference to the data. Please refer to :ref:`sardana-2dcontroller-valuereferencing` chapter from -:ref:`sardana-2dcontroller-howto` in order to implement this feature for 1D +:ref:`sardana-2dcontroller-howto` guide in order to implement this feature for 1D controller. .. _ALBA: http://www.cells.es/ From 1157690d89d6701684d4269b796444e4a1f1d03f Mon Sep 17 00:00:00 2001 From: reszelaz Date: Thu, 4 Feb 2021 11:48:30 +0100 Subject: [PATCH 48/82] Fix typo --- doc/source/devel/howto_controllers/howto_2dcontroller.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/devel/howto_controllers/howto_2dcontroller.rst b/doc/source/devel/howto_controllers/howto_2dcontroller.rst index daf6d97cf1..e7510c19c2 100644 --- a/doc/source/devel/howto_controllers/howto_2dcontroller.rst +++ b/doc/source/devel/howto_controllers/howto_2dcontroller.rst @@ -38,7 +38,7 @@ sensor size or dynamic e.g. depending on the detector's (or an intermediate control software layer e.g. `LImA`_) configuration like :term:`RoI` or binning. In any case you must provide the shape in the format of a two-element sequence -with horizonatal and vertical dimensions using +with horizontal and vertical dimensions using the :meth:`~sardana.pool.controller.Controller.GetAxisPar` method. Here is an example of the possible implementation of From 15633e092d47f927725ee509e52800658519f4f5 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Thu, 4 Feb 2021 11:49:23 +0100 Subject: [PATCH 49/82] Fix typo --- .../devel/howto_controllers/howto_pseudocountercontroller.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/devel/howto_controllers/howto_pseudocountercontroller.rst b/doc/source/devel/howto_controllers/howto_pseudocountercontroller.rst index 4a8c6b1b3d..49bbe2cc27 100644 --- a/doc/source/devel/howto_controllers/howto_pseudocountercontroller.rst +++ b/doc/source/devel/howto_controllers/howto_pseudocountercontroller.rst @@ -105,7 +105,7 @@ controller must provide the shape of the calculation result in either of the formats: - one-element sequence with the length of the spectrum -- two-element sequence with the horizonatal and vertical dimensions of the image +- two-element sequence with the horizontal and vertical dimensions of the image using the :meth:`~sardana.pool.controller.Controller.GetAxisPar` method. From 77b1177dbc3e64079a8f69255131fadc0b5867d0 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Feb 2021 22:59:04 +0100 Subject: [PATCH 50/82] Improve backwards compat. to auto-discover shape In case the controller parameter "shape" is not implemented the backwards compatibility layer tries to read the value attribute and determine the shape from the value. Adds another kind of fallback and try to get it from the max_dim_size of the value attribute. --- src/sardana/pool/poolbasechannel.py | 34 +++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/sardana/pool/poolbasechannel.py b/src/sardana/pool/poolbasechannel.py index 631d5955e6..34624a5f2c 100644 --- a/src/sardana/pool/poolbasechannel.py +++ b/src/sardana/pool/poolbasechannel.py @@ -650,6 +650,23 @@ def set_integration_time(self, integration_time, propagate=1): # shape # ------------------------------------------------------------------------- + def _get_shape_from_max_dim_size(self): + # MaxDimSize could actually become a standard fallback + # in case the shape controller parameters is not implemented + # reconsider it whenever backwards compatibility with reading the value + # is about to be removed + try: + from sardana.pool.controller import MaxDimSize + controller = self.controller + axis_attr_info = controller.get_axis_attributes(self.axis) + value_info = axis_attr_info["Value"] + shape = value_info[MaxDimSize] + except Exception as e: + raise RuntimeError( + "can not provide backwards compatibility, you must" + "implement shape axis parameter") from e + return shape + def get_shape(self, cache=True, propagate=1): if not cache or self._shape is None: shape = self.read_shape() @@ -677,13 +694,16 @@ def read_shape(self): "not implementing shape axis parameter in 1D and 2D " "controllers is deprecated since Jan21") value = self.value.value - import numpy - try: - shape = numpy.shape(value) - except Exception as e: - raise RuntimeError( - "can not provide backwards compatibility, you must" - "implement shape axis parameter") from e + if value is None: + self.debug("could not get shape from value") + shape = self._get_shape_from_max_dim_size() + else: + import numpy + try: + shape = numpy.shape(value) + except Exception: + self.debug("could not get shape from value") + shape = self._get_shape_from_max_dim_size() # scalar channel else: shape = [] From 27124fe667def79d60d9a78c2cb01541ba0b1b70 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 9 Feb 2021 11:34:05 +0100 Subject: [PATCH 51/82] Add note on backwards compatibility with MaxDimSize --- .../devel/howto_controllers/howto_1dcontroller.rst | 8 ++++++-- .../devel/howto_controllers/howto_2dcontroller.rst | 8 ++++++-- .../howto_controllers/howto_pseudocountercontroller.rst | 9 ++++++++- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/doc/source/devel/howto_controllers/howto_1dcontroller.rst b/doc/source/devel/howto_controllers/howto_1dcontroller.rst index 429ac416d6..90f5a341d5 100644 --- a/doc/source/devel/howto_controllers/howto_1dcontroller.rst +++ b/doc/source/devel/howto_controllers/howto_1dcontroller.rst @@ -35,12 +35,12 @@ differences explained in continuation. Get 1D shape ~~~~~~~~~~~~ -1D controller must provide a shape of the spectrum which will be produced by +1D controller should provide a shape of the spectrum which will be produced by acquisition. The shape can be either static e.g. defined by the detector's sensor size or dynamic e.g. depending on the detector's (or an intermediate control software layer e.g. `LImA`_) configuration like :term:`RoI` or binning. -In any case you must provide the shape in the format of a one-element sequence +In any case you should provide the shape in the format of a one-element sequence with the length of the spectrum using the :meth:`~sardana.pool.controller.Controller.GetAxisPar` method. @@ -55,6 +55,10 @@ Here is an example of the possible implementation of if par == "shape": return self.springfield.getShape(axis) +For backwards compatibility, in case of not implementing the ``shape`` axis +parameter, shape will be determined from the ``MaxDimSize`` of the ``Value`` +attribute, currently (4096,). + .. _sardana-1dcontroller-differences-countertimer: Differences with counter/timer controller diff --git a/doc/source/devel/howto_controllers/howto_2dcontroller.rst b/doc/source/devel/howto_controllers/howto_2dcontroller.rst index e7510c19c2..fa6d42d80b 100644 --- a/doc/source/devel/howto_controllers/howto_2dcontroller.rst +++ b/doc/source/devel/howto_controllers/howto_2dcontroller.rst @@ -32,12 +32,12 @@ differences explained in continuation. Get 2D shape ~~~~~~~~~~~~ -2D controller must provide a shape of the image which will be produced by +2D controller should provide a shape of the image which will be produced by acquisition. The shape can be either static e.g. defined by the detector's sensor size or dynamic e.g. depending on the detector's (or an intermediate control software layer e.g. `LImA`_) configuration like :term:`RoI` or binning. -In any case you must provide the shape in the format of a two-element sequence +In any case you should provide the shape in the format of a two-element sequence with horizontal and vertical dimensions using the :meth:`~sardana.pool.controller.Controller.GetAxisPar` method. @@ -52,6 +52,10 @@ Here is an example of the possible implementation of if par == "shape": return self.springfield.getShape(axis) +For backwards compatibility, in case of not implementing the ``shape`` axis +parameter, shape will be determined frm the ``MaxDimSize`` of the ``Value`` +attribute, currently (4096, 4096). + .. _sardana-2dcontroller-differences-countertimer: Differences with counter/timer controller diff --git a/doc/source/devel/howto_controllers/howto_pseudocountercontroller.rst b/doc/source/devel/howto_controllers/howto_pseudocountercontroller.rst index 49bbe2cc27..1a55a4b862 100644 --- a/doc/source/devel/howto_controllers/howto_pseudocountercontroller.rst +++ b/doc/source/devel/howto_controllers/howto_pseudocountercontroller.rst @@ -73,6 +73,8 @@ negative. The value close to the zero indicates the beam centered in the middle. Similarly behaves the horizontal pseudo counter. The total pseudo counter is the mean value of all the four sensors and indicates the beam intensity. +.. _sardana-pseudocountercontroller-howto-changing-interface: + Changing default interface -------------------------- @@ -101,7 +103,7 @@ Get pseudo counter shape ------------------------ If you change the pseudo counter format to spectrum or image then you -controller must provide the shape of the calculation result in either +controller should provide the shape of the calculation result in either of the formats: - one-element sequence with the length of the spectrum @@ -118,6 +120,11 @@ Here is an example of the possible implementation of if par == "shape": return [1024, 1024] + +For backwards compatibility, in case of not implementing the ``shape`` axis +parameter, shape will be determined from the ``MaxDimSize`` of the ``Value`` +attribute as defined in :ref:`sardana-pseudocountercontroller-howto-changing-interface`. + Including external variables in the calculation ----------------------------------------------- From fc64b51b49ecdac98e9739209b56b34c6a4db4f7 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Tue, 9 Feb 2021 16:19:38 +0100 Subject: [PATCH 52/82] Update CHANGELOG.md --- CHANGELOG.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1315493d49..8b2789011b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ This file follows the formats and conventions from [keepachangelog.com] ### Added +* `shape` controller axis parameter (plugin), `shape` experimental channel + attribute (kernel) and `Shape` Tango attribute to the experimental channels + (#1296, #1466) * *scan information* and *scan point* forms to the *showscan online* widget (#1386, #1477, #1479) * `ScanPlotWidget`, `ScanPlotWindow`, `ScanInfoForm`, `ScanPointForm` and `ScanWindow` widget classes for easier composition of custom GUIs involving online scan plotting (#1386) @@ -18,6 +21,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Initial delay in position domain to the synchronization description in *ct* like continuous scans (#1428) * Avoid double printing of user units in PMTV: read widget and units widget (#1424) +* Documentation on how to write 1D and 2D controllers (#1494) * Missing documentation of SEP18 concepts to how-to counter/timer controller (#995, #1492) * Document how to properly deal with exceptions in macros in order to not interfer with macro stopping/aborting (#1461) @@ -46,6 +50,15 @@ This file follows the formats and conventions from [keepachangelog.com] * Make write of MeasurementGroup (Taurus extension) integration time more robust (#1473) * String formatting when rising exceptions in pseudomotors (#1469) +### Changed + +* Experimental channel shape is now considered as a result of the configuration + and not part of the measurement group configuration (#1296, #1466) + +### Removed + +* `shape` from the measurement group configuration and `expconf` (#1296, #1466) + ## [3.0.3] 2020-09-18 ### Added From 98d351148424a31a74baf777d2fd15ff32a7c7f7 Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 10 Feb 2021 22:05:21 +0100 Subject: [PATCH 53/82] Fill parent_macro in case of executing XML hooks (sequencer) XML hooks, usually composed by the sequencer, as other hooks (programmatic or general), needs information about the parent macro. Fill it when preparing the macro object. See #1472 for more details. --- src/sardana/macroserver/msmacromanager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py index 7b95b8fd7c..43cc00d04c 100644 --- a/src/sardana/macroserver/msmacromanager.py +++ b/src/sardana/macroserver/msmacromanager.py @@ -1613,8 +1613,9 @@ def __runStatelessXML(self, xml=None): def __runXMLMacro(self, xml): assert xml.tag == 'macro' + parent_macro = self.getRunningMacro() try: - macro_obj, _ = self._prepareXMLMacro(xml) + macro_obj, _ = self._prepareXMLMacro(xml, parent_macro) except AbortException as ae: raise ae except Exception as e: From f5edb4f47ca86e56fe80d9db946e2a3569f0248b Mon Sep 17 00:00:00 2001 From: reszelaz Date: Thu, 11 Feb 2021 11:25:08 +0100 Subject: [PATCH 54/82] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b2789011b..a64ae85da6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ This file follows the formats and conventions from [keepachangelog.com] e.g. pseudo counter's value, in controllers (#1440, #1446) * Storing string values in PreScanSnapshot in NXscanH5_FileRecorder (#1486) * Storing string values as custom data in NXscanH5_FileRecorder (#1485) +* Fill parent_macro in case of executing XML hooks e.g. in sequencer (#1497) * Problems with macro id's when `sequencer` executes from _plain text_ files (#1215, #1216) * `sequencer` loading of plain text sequences in spock syntax with macro functions (#1422) * Allow running Spock without Qt bindings (#1462, #1463) From 851307e4920d68ee2c5f90610be1b12dcad6bd3b Mon Sep 17 00:00:00 2001 From: zreszela Date: Wed, 17 Feb 2021 12:30:43 +0100 Subject: [PATCH 55/82] Update link to Sardana plugins catalogue --- doc/source/users/adding_elements.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/source/users/adding_elements.rst b/doc/source/users/adding_elements.rst index 198d42732b..21353ee9f4 100644 --- a/doc/source/users/adding_elements.rst +++ b/doc/source/users/adding_elements.rst @@ -7,7 +7,8 @@ Adding real elements For the sake of demonstration, let's suppose you want to integrate a slit with IcePAP-based motors into Sardana. -Before doing anything else you should check the `controller plugin register `_. +Before doing anything else you should check the +`Sardana plugins catalogue `_. There's a high chance that somebody already wrote the plugin for your hardware. If not, you can :ref:`write the plugin yourself `. From 8f84ea4c8b03782a4b101639833c048ffb33f18c Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 19 Feb 2021 14:42:31 +0100 Subject: [PATCH 56/82] Ensure order of moveables is preserved in Motion object Move of motion object sends motors to wrong positions. This happens when using moveables from different Pools and is not 100 % reproducible. This is due to the fact that we rely on simple dictionaries (their order is not preserved in Python 3.5) when constructing the Motion object from the list of moveables requested by the user. Further move requests on that object assume that the positions correspond to the order of moveables requested at the Motion object creation time. Currently the order may be lost. Use OrderedDict instead to avoid such problems. --- src/sardana/taurus/core/tango/sardana/motion.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/motion.py b/src/sardana/taurus/core/tango/sardana/motion.py index ace214903e..cc28ae9492 100644 --- a/src/sardana/taurus/core/tango/sardana/motion.py +++ b/src/sardana/taurus/core/tango/sardana/motion.py @@ -30,6 +30,7 @@ __docformat__ = 'restructuredtext' import time +from collections import OrderedDict from taurus.core.util.containers import CaselessDict @@ -270,7 +271,7 @@ def init_by_names(self, names, moveable_srcs, allow_repeat, allow_unknown): allow_repeat=allow_repeat, allow_unknown=allow_unknown) # map - ms_moveables = {} + ms_moveables = OrderedDict() for moveable_source, ms_names in list(ms_elem_names.items()): moveable = moveable_source.getMoveable(ms_names) ms_moveables[moveable_source] = moveable @@ -330,7 +331,7 @@ def getElemNamesByMoveableSource(self, names, moveable_sources, belong to the that motion source. """ - ms_elems = {} + ms_elems = OrderedDict() for name in names: moveable = None From 6e09a749e271a50f590da208462f1da6db066780 Mon Sep 17 00:00:00 2001 From: zreszela Date: Fri, 19 Feb 2021 18:49:25 +0100 Subject: [PATCH 57/82] Fix getting macroserver from remote door _get_macroserver_for_door() is assuming that the macroserver belongs to the default Tango database. In case the door is a remote door (on different Tango database) the macroserver is never found. Fix it by interrogating the Tango database of the door and by creating a macroserver device with full name. --- src/sardana/taurus/core/tango/sardana/macroserver.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/macroserver.py b/src/sardana/taurus/core/tango/sardana/macroserver.py index 4518b88d60..783d558b5c 100644 --- a/src/sardana/taurus/core/tango/sardana/macroserver.py +++ b/src/sardana/taurus/core/tango/sardana/macroserver.py @@ -424,11 +424,10 @@ def macro_server(self): def _get_macroserver_for_door(self): """Returns the MacroServer device object in the same DeviceServer as this door""" - db = self.factory().getDatabase() + db = self.getParentObj() door_name = self.dev_name() server_list = list(db.get_server_list('MacroServer/*')) server_list += list(db.get_server_list('Sardana/*')) - server_devs = None for server in server_list: server_devs = db.get_device_class_list(server) devs, klasses = server_devs[0::2], server_devs[1::2] @@ -436,7 +435,8 @@ def _get_macroserver_for_door(self): if dev.lower() == door_name: for i, klass in enumerate(klasses): if klass == 'MacroServer': - return self.factory().getDevice(devs[i]) + full_name = db.getFullName() + "/" + devs[i] + return self.factory().getDevice(full_name) else: return None From 3e749f9a50ee21ca479e55828f370e385f42d702 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 22 Feb 2021 17:09:49 +0100 Subject: [PATCH 58/82] Assert motor sign is -1 or 1 Setting motor sign to 0 may lead to wrong position calculation. Assert motor sign is -1 or 1. Fix #1345. --- src/sardana/pool/poolmotor.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sardana/pool/poolmotor.py b/src/sardana/pool/poolmotor.py index da281c21d3..bba40d11bc 100644 --- a/src/sardana/pool/poolmotor.py +++ b/src/sardana/pool/poolmotor.py @@ -417,6 +417,8 @@ def get_sign(self, cache=True): return self._sign def set_sign(self, sign, propagate=1): + assert sign in (-1, 1), \ + "sign must be either -1 or 1 (not {})".format(sign) old_sign = self._sign.value self._sign.set_value(sign, propagate=propagate) # invert lower with upper limit switches and send event in case of From 98bd4565b7e1422844e25823f21f033f0497db73 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 22 Feb 2021 22:10:00 +0100 Subject: [PATCH 59/82] Document sardanacustomsettings.LOG_BCK_COUNT --- src/sardana/sardanacustomsettings.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/sardana/sardanacustomsettings.py b/src/sardana/sardanacustomsettings.py index 97c4859628..b0e75f9d72 100644 --- a/src/sardana/sardanacustomsettings.py +++ b/src/sardana/sardanacustomsettings.py @@ -37,10 +37,13 @@ #: UnitTests Pool Device name: Pool Device to use in unit tests. UNITTEST_POOL_NAME = "pool/demo1/1" -#: Size and number of rotating backups of the log files. -#: The Pool and MacroServer Device servers will use these values for their -#: logs. +#: Size of rotating backups of the log files. +#: The Pool, MacroServer and Sardana device servers will use these values +#: for their logs. LOG_FILES_SIZE = 1e7 +#: Number of rotating backups of the log files. +#: The Pool, MacroServer and Sardana device servers will use these values +#: for their logs. LOG_BCK_COUNT = 5 #: Input handler for spock interactive macros. Accepted values are: From b07d73016472869dc4b021ab76dd6d6e21765e8f Mon Sep 17 00:00:00 2001 From: reszelaz Date: Wed, 24 Feb 2021 15:12:56 +0100 Subject: [PATCH 60/82] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a64ae85da6..c700b6e78f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Initial delay in position domain to the synchronization description in *ct* like continuous scans (#1428) * Avoid double printing of user units in PMTV: read widget and units widget (#1424) +* Assert motor sign is -1 or 1 (#1345, #1507) * Documentation on how to write 1D and 2D controllers (#1494) * Missing documentation of SEP18 concepts to how-to counter/timer controller (#995, #1492) * Document how to properly deal with exceptions in macros in order to not interfer From 77b7302cedb1417c5ea864d3190d4f65eea19c8e Mon Sep 17 00:00:00 2001 From: reszelaz Date: Wed, 24 Feb 2021 15:16:05 +0100 Subject: [PATCH 61/82] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c700b6e78f..cb59c04153 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Problems with macro id's when `sequencer` executes from _plain text_ files (#1215, #1216) * `sequencer` loading of plain text sequences in spock syntax with macro functions (#1422) * Allow running Spock without Qt bindings (#1462, #1463) +* Fix getting macroserver from remote door in Sardana-Taurus Door extension (#1506) * Use equality instead of identity checks for numbers and strings (#1491) * Docstring of QtSpockWidget (#1484) * Recorders tests helpers (#1439) From bc6f55179d4c45f854b8f702ebdca546baab62da Mon Sep 17 00:00:00 2001 From: reszelaz Date: Wed, 24 Feb 2021 15:27:00 +0100 Subject: [PATCH 62/82] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb59c04153..8bf8ada24b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Execute per measurement preparation in `mesh` scan macro (#1437) * Continously read value references in hardware synchronized acquisition instead of reading only at the end (#1442, #1448) +* Ensure order of moveables is preserved in Motion object (#1505) * Avoid problems when defining different, e.g. shape, standard attributes, e.g. pseudo counter's value, in controllers (#1440, #1446) * Storing string values in PreScanSnapshot in NXscanH5_FileRecorder (#1486) From ff77fe8c618812d465543b7918ac2e37b0a47b46 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 8 Mar 2021 11:55:47 +0100 Subject: [PATCH 63/82] Open msenvironment shelve file always with 'c' flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Empty environment files used with dumb backend fails to open with 'w' flag. This does not happen for gdbm backend. Avoid this problem by always using 'c' flag, which according to docs [1]: "Open database for reading and writing, creating it if it doesn’t exist". Tested and it does not harm when the environment shelve files is not empty - its content is preserved. Fixes #1425. [1] https://docs.python.org/3/library/dbm.html#dbm.open --- src/sardana/macroserver/msenvmanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/macroserver/msenvmanager.py b/src/sardana/macroserver/msenvmanager.py index ac43dd5c20..64b5499214 100644 --- a/src/sardana/macroserver/msenvmanager.py +++ b/src/sardana/macroserver/msenvmanager.py @@ -145,7 +145,7 @@ def setEnvironmentDb(self, f_name): raise ose if os.path.exists(f_name) or os.path.exists(f_name + ".dat"): try: - self._env = shelve.open(f_name, flag='w', writeback=False) + self._env = shelve.open(f_name, flag='c', writeback=False) except Exception: self.error("Failed to access environment in %s", f_name) self.debug("Details:", exc_info=1) From a21b740b285501104533bc0a7516f6d388b2c676 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 8 Mar 2021 15:47:46 +0100 Subject: [PATCH 64/82] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bf8ada24b..3bb80aea8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,7 @@ This file follows the formats and conventions from [keepachangelog.com] * `sequencer` loading of plain text sequences in spock syntax with macro functions (#1422) * Allow running Spock without Qt bindings (#1462, #1463) * Fix getting macroserver from remote door in Sardana-Taurus Door extension (#1506) +* MacroServer opening empty environment files used with dumb backend (#1425, #1514) * Use equality instead of identity checks for numbers and strings (#1491) * Docstring of QtSpockWidget (#1484) * Recorders tests helpers (#1439) From 98b12d0dcb61b2de034f7cb79cfaeb1114473696 Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 11 Mar 2021 09:43:07 +0100 Subject: [PATCH 65/82] docs: fix formatting in User's Overview --- doc/source/users/overview.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/source/users/overview.rst b/doc/source/users/overview.rst index ac5e88907c..54df2bfe3b 100644 --- a/doc/source/users/overview.rst +++ b/doc/source/users/overview.rst @@ -7,7 +7,11 @@ Overview Sardana is the control program initially developed at ALBA_. Our mission statement: - `Produce a modular, high performance, robust, and generic user environment for control applications in large and small installations. Make Sardana the generic user environment distributed in the Tango project and the standard basis of collaborations in control.` + Produce a modular, high performance, robust, and generic user environment + for control applications in large and small installations. + Make Sardana the generic user environment distributed in the Tango project + and the standard basis of collaborations in control. + Up to now, control applications in large installations have been notoriously difficult to share. Inspired by the success of the Tango_ collaboration, ALBA_ From 03f4fc6a9aa83224640b356b90e33f0383ebe2dd Mon Sep 17 00:00:00 2001 From: reszelaz Date: Thu, 11 Mar 2021 13:24:26 +0100 Subject: [PATCH 66/82] Revert "Open msenvironment shelve file always with 'c' flag" --- src/sardana/macroserver/msenvmanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sardana/macroserver/msenvmanager.py b/src/sardana/macroserver/msenvmanager.py index 64b5499214..ac43dd5c20 100644 --- a/src/sardana/macroserver/msenvmanager.py +++ b/src/sardana/macroserver/msenvmanager.py @@ -145,7 +145,7 @@ def setEnvironmentDb(self, f_name): raise ose if os.path.exists(f_name) or os.path.exists(f_name + ".dat"): try: - self._env = shelve.open(f_name, flag='c', writeback=False) + self._env = shelve.open(f_name, flag='w', writeback=False) except Exception: self.error("Failed to access environment in %s", f_name) self.debug("Details:", exc_info=1) From 4ef2756b90c0f656cd565a6d823d9338af7a95e3 Mon Sep 17 00:00:00 2001 From: zreszela Date: Thu, 11 Mar 2021 23:49:25 +0100 Subject: [PATCH 67/82] Fix msenvironment when empty and used with dumb backend Empty environments used with the dumb backend, when this is never correctly closed [*], fails to open with the 'w' flag. This is because the `.dir` file is never created and it is necessary to determine the backend by the `dbm.whichdb()`. Avoid this problem by always closing the dbm. Actually this is only necessary for the dumb backend but do it for gnu as well - it does not harm and makes the code cleaner. [*] The MacroServer process currently crashes at exit - see: sardana-org/sardana#719 Fixes #1425. --- src/sardana/macroserver/msenvmanager.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/sardana/macroserver/msenvmanager.py b/src/sardana/macroserver/msenvmanager.py index ac43dd5c20..46da8b5056 100644 --- a/src/sardana/macroserver/msenvmanager.py +++ b/src/sardana/macroserver/msenvmanager.py @@ -53,7 +53,7 @@ def _dbm_dumb(filename): return dbm.dumb.open(filename, "c") -def _dbm_shelve(filename, backend): +def _create_dbm(filename, backend): if backend is None: try: return _dbm_gnu(filename) @@ -143,22 +143,22 @@ def setEnvironmentDb(self, f_name): self.error("Creating environment: %s" % ose.strerror) self.debug("Details:", exc_info=1) raise ose - if os.path.exists(f_name) or os.path.exists(f_name + ".dat"): - try: - self._env = shelve.open(f_name, flag='w', writeback=False) - except Exception: - self.error("Failed to access environment in %s", f_name) - self.debug("Details:", exc_info=1) - raise - else: + if not os.path.exists(f_name) and not os.path.exists(f_name + ".dat"): backend = getattr(sardanacustomsettings, "MS_ENV_SHELVE_BACKEND", None) try: - self._env = shelve.Shelf(_dbm_shelve(f_name, backend)) + dbm = _create_dbm(f_name, backend) + dbm.close() except Exception: self.error("Failed to create environment in %s", f_name) self.debug("Details:", exc_info=1) raise + try: + self._env = shelve.open(f_name, flag='w', writeback=False) + except Exception: + self.error("Failed to access environment in %s", f_name) + self.debug("Details:", exc_info=1) + raise self.info("Environment is being stored in %s", f_name) From f0f12d477011c757287a9358bda3ff92840310f6 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Fri, 12 Mar 2021 12:06:23 +0100 Subject: [PATCH 68/82] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bb80aea8c..ba5c4648b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,7 +46,7 @@ This file follows the formats and conventions from [keepachangelog.com] * `sequencer` loading of plain text sequences in spock syntax with macro functions (#1422) * Allow running Spock without Qt bindings (#1462, #1463) * Fix getting macroserver from remote door in Sardana-Taurus Door extension (#1506) -* MacroServer opening empty environment files used with dumb backend (#1425, #1514) +* MacroServer opening empty environment files used with dumb backend (#1425, #1514, #1517, #1520) * Use equality instead of identity checks for numbers and strings (#1491) * Docstring of QtSpockWidget (#1484) * Recorders tests helpers (#1439) From a805a8246db674f65dc1df12473ab22f0e81c4f4 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 15 Mar 2021 11:35:00 +0100 Subject: [PATCH 69/82] test: add Hookable tests Add test to reveal problem with setting hooks to an empty list. --- src/sardana/macroserver/test/test_macro.py | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/sardana/macroserver/test/test_macro.py diff --git a/src/sardana/macroserver/test/test_macro.py b/src/sardana/macroserver/test/test_macro.py new file mode 100644 index 0000000000..074b0ae1b7 --- /dev/null +++ b/src/sardana/macroserver/test/test_macro.py @@ -0,0 +1,8 @@ +from sardana.macroserver.macro import Hookable + + +def test_Hookable(): + hookable = Hookable() + assert len(hookable.hooks) == 0 + hookable.hooks = [] + assert len(hookable.hooks) == 0 \ No newline at end of file From d48109854fbd8eb97d04295e82178cae5db61f1b Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 15 Mar 2021 11:41:46 +0100 Subject: [PATCH 70/82] fix: Hookable setting hooks to empty list --- src/sardana/macroserver/macro.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sardana/macroserver/macro.py b/src/sardana/macroserver/macro.py index afb91c0d3a..e7d4b52450 100644 --- a/src/sardana/macroserver/macro.py +++ b/src/sardana/macroserver/macro.py @@ -283,6 +283,8 @@ def hooks(self, hooks): # delete _hookHintsDict to force its recreation on the next access if hasattr(self, '_hookHintsDict'): del self._hookHintsDict + if len(self._hooks) == 0: + return # create _hookHintsDict self._getHookHintsDict()['_ALL_'] = list(zip(*self._hooks))[0] nohints = self._hookHintsDict['_NOHINTS_'] From 36116d38d02b36e9b33336bc635f30e92526a096 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 15 Mar 2021 11:42:48 +0100 Subject: [PATCH 71/82] test: enhance Hookable test to get unexisting hook place --- src/sardana/macroserver/test/test_macro.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sardana/macroserver/test/test_macro.py b/src/sardana/macroserver/test/test_macro.py index 074b0ae1b7..0c9f483292 100644 --- a/src/sardana/macroserver/test/test_macro.py +++ b/src/sardana/macroserver/test/test_macro.py @@ -4,5 +4,8 @@ def test_Hookable(): hookable = Hookable() assert len(hookable.hooks) == 0 + hooks = hookable.getHooks("unexisting-hook-place") + assert len(hooks) == 0 hookable.hooks = [] - assert len(hookable.hooks) == 0 \ No newline at end of file + assert len(hookable.hooks) == 0 + hooks = hookable.getHooks("unexisting-hook-place") From dec4af7c1fc1d5e37a77f1279cc80068438deff8 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 15 Mar 2021 11:46:18 +0100 Subject: [PATCH 72/82] test: add missing header to test_macro.py --- src/sardana/macroserver/test/test_macro.py | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/sardana/macroserver/test/test_macro.py b/src/sardana/macroserver/test/test_macro.py index 0c9f483292..2b6ee0b54e 100644 --- a/src/sardana/macroserver/test/test_macro.py +++ b/src/sardana/macroserver/test/test_macro.py @@ -1,3 +1,28 @@ +#!/usr/bin/env python + +############################################################################## +## +# This file is part of Sardana +## +# http://www.sardana-controls.org/ +## +# Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain +## +# Sardana is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +## +# Sardana is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +## +# You should have received a copy of the GNU Lesser General Public License +# along with Sardana. If not, see . +## +############################################################################## + from sardana.macroserver.macro import Hookable From a6b55a6e5f259fff9c251411c626d735a11a0af7 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 15 Mar 2021 11:46:34 +0100 Subject: [PATCH 73/82] test: add missing header to test_msparameter.py --- .../macroserver/test/test_msparameter.py | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/sardana/macroserver/test/test_msparameter.py b/src/sardana/macroserver/test/test_msparameter.py index 3151081d39..07f2ece2bf 100644 --- a/src/sardana/macroserver/test/test_msparameter.py +++ b/src/sardana/macroserver/test/test_msparameter.py @@ -1,3 +1,28 @@ +#!/usr/bin/env python + +############################################################################## +## +# This file is part of Sardana +## +# http://www.sardana-controls.org/ +## +# Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain +## +# Sardana is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +## +# Sardana is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +## +# You should have received a copy of the GNU Lesser General Public License +# along with Sardana. If not, see . +## +############################################################################## + import unittest from taurus.test import insertTest From 497cf2ba041bd083b831f90e317b13a88e7efa33 Mon Sep 17 00:00:00 2001 From: zreszela Date: Mon, 15 Mar 2021 11:51:55 +0100 Subject: [PATCH 74/82] test: add missing assert to test_Hookable --- src/sardana/macroserver/test/test_macro.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sardana/macroserver/test/test_macro.py b/src/sardana/macroserver/test/test_macro.py index 2b6ee0b54e..7f21ce2c32 100644 --- a/src/sardana/macroserver/test/test_macro.py +++ b/src/sardana/macroserver/test/test_macro.py @@ -34,3 +34,4 @@ def test_Hookable(): hookable.hooks = [] assert len(hookable.hooks) == 0 hooks = hookable.getHooks("unexisting-hook-place") + assert len(hooks) == 0 From 7e828eaadb8b0304da07831a62d0d3d1ea528492 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Mon, 15 Mar 2021 13:00:50 +0100 Subject: [PATCH 75/82] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba5c4648b3..7111e25be3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Allow running Spock without Qt bindings (#1462, #1463) * Fix getting macroserver from remote door in Sardana-Taurus Door extension (#1506) * MacroServer opening empty environment files used with dumb backend (#1425, #1514, #1517, #1520) +* Setting `Hookable.hooks` to empty list (#1522) * Use equality instead of identity checks for numbers and strings (#1491) * Docstring of QtSpockWidget (#1484) * Recorders tests helpers (#1439) From 66fc20e78aeb6b160e6488e0ee09e6656fdabacb Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 16 Mar 2021 12:27:20 +0100 Subject: [PATCH 76/82] docs: add sardana.taurus.core.tango.sardana.macro to API --- .../api/sardana/taurus/core/tango/sardana.rst | 1 + .../taurus/core/tango/sardana/macro.rst | 37 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 doc/source/devel/api/sardana/taurus/core/tango/sardana/macro.rst diff --git a/doc/source/devel/api/sardana/taurus/core/tango/sardana.rst b/doc/source/devel/api/sardana/taurus/core/tango/sardana.rst index 9912a909b5..65e6b75b8e 100644 --- a/doc/source/devel/api/sardana/taurus/core/tango/sardana.rst +++ b/doc/source/devel/api/sardana/taurus/core/tango/sardana.rst @@ -22,6 +22,7 @@ pool macroserver + macro .. autofunction:: registerExtensions .. autofunction:: unregisterExtensions diff --git a/doc/source/devel/api/sardana/taurus/core/tango/sardana/macro.rst b/doc/source/devel/api/sardana/taurus/core/tango/sardana/macro.rst new file mode 100644 index 0000000000..9b14d3b524 --- /dev/null +++ b/doc/source/devel/api/sardana/taurus/core/tango/sardana/macro.rst @@ -0,0 +1,37 @@ +.. currentmodule:: sardana.taurus.core.tango.sardana.macro + + +:mod:`~sardana.taurus.core.tango.sardana.macro` +=============================================== + +.. automodule:: sardana.taurus.core.tango.sardana.macro + +.. rubric:: Classes + +.. hlist:: + :columns: 4 + + * :class:`Macro` + * :class:`MacroInfo` + +Macro +----- + +.. inheritance-diagram:: Macro + :parts: 1 + +.. autoclass:: Macro + :show-inheritance: + :members: + :undoc-members: + +MacroInfo +--------- + +.. inheritance-diagram:: MacroInfo + :parts: 1 + +.. autoclass:: MacroInfo + :show-inheritance: + :members: + :undoc-members: From 19cf2e57a2930b245954b17616b4fb5ad162d0ac Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 16 Mar 2021 12:48:49 +0100 Subject: [PATCH 77/82] docs: document allowed hooks in macro documentation (client) For example, print "allowed hooks" in macro description in Spock. --- .../taurus/core/tango/sardana/macro.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/sardana/taurus/core/tango/sardana/macro.py b/src/sardana/taurus/core/tango/sardana/macro.py index 4c497d121d..3db0a9ae07 100644 --- a/src/sardana/taurus/core/tango/sardana/macro.py +++ b/src/sardana/taurus/core/tango/sardana/macro.py @@ -89,6 +89,9 @@ def _buildDoc(self): if self.hasResult(): doc += '\n\nResult:\n\t' doc += '\n\t'.join(self.getResultDescr()) + if self.allowsHooks(): + doc += '\n\nAllows hooks:\n\t' + doc += '\n\t'.join(self.getAllowedHooks()) self.doc = doc def _hasParamComplex(self, parameters=None): @@ -299,6 +302,31 @@ def formatResult(self, result): else: return tuple(res) + def allowsHooks(self): + """Checks whether the macro allows hooks + + :return: True or False depending if the macro allows hooks + :rtype: bool + """ + try: + self.hints["allowsHooks"] + except KeyError: + return False + return True + + def getAllowedHooks(self): + """Gets allowed hooks + + :return: list with the allowed hooks or empty list if the macro + does not allow hooks + :rtype: list[str] + """ + try: + allowed_hooks = self.hints["allowsHooks"] + except KeyError: + return [] + return allowed_hooks + def __str__(self): return self.name From c40c6aa53e970ceb9a10b91d573bda993b4aa287 Mon Sep 17 00:00:00 2001 From: zreszela Date: Tue, 16 Mar 2021 13:28:48 +0100 Subject: [PATCH 78/82] fix: Macro.hasResult() and Macro.hasParams() Macro.hasResult() and Macro.hasParams() wrongly returns True if the result and params definition is an empty list. This leads to adding empty: "Parameters" and "Result" sections in the macro's description in Spock. Fix it and consider empty lists as no parameters and result. --- src/sardana/taurus/core/tango/sardana/macro.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sardana/taurus/core/tango/sardana/macro.py b/src/sardana/taurus/core/tango/sardana/macro.py index 4c497d121d..3948a8a46b 100644 --- a/src/sardana/taurus/core/tango/sardana/macro.py +++ b/src/sardana/taurus/core/tango/sardana/macro.py @@ -137,7 +137,7 @@ def hasParams(self): :return: (bool) True if the macro has parameters or False otherwise """ - return hasattr(self, 'parameters') + return hasattr(self, 'parameters') and len(self.parameters) > 0 def getParamList(self): """Returs the list of parameters @@ -223,7 +223,7 @@ def hasResult(self): :return: (bool) True if the macro has a result or False otherwise """ - return hasattr(self, 'result') + return hasattr(self, 'result') and len(self.result) > 0 def getResultList(self): """Returns the list of results From d1bfb86fe2fd71ea225485bf2b533d089268938c Mon Sep 17 00:00:00 2001 From: reszelaz Date: Wed, 17 Mar 2021 11:04:10 +0100 Subject: [PATCH 79/82] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7111e25be3..daf07d82f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,8 @@ This file follows the formats and conventions from [keepachangelog.com] * Fix getting macroserver from remote door in Sardana-Taurus Door extension (#1506) * MacroServer opening empty environment files used with dumb backend (#1425, #1514, #1517, #1520) * Setting `Hookable.hooks` to empty list (#1522) +* `Macro.hasResult()` and `Macro.hasParams()` what avoids adding empty _Parameters_ and _Result_ + sections in the macro description in Spock (#1524) * Use equality instead of identity checks for numbers and strings (#1491) * Docstring of QtSpockWidget (#1484) * Recorders tests helpers (#1439) From 327bbd29edf80609a845d7cbea7e0fe537ca969c Mon Sep 17 00:00:00 2001 From: reszelaz Date: Wed, 17 Mar 2021 11:06:24 +0100 Subject: [PATCH 80/82] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index daf07d82f7..11d03bb5b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ This file follows the formats and conventions from [keepachangelog.com] * Initial delay in position domain to the synchronization description in *ct* like continuous scans (#1428) * Avoid double printing of user units in PMTV: read widget and units widget (#1424) +* Allowed hooks to macro description in Spock (#1523) * Assert motor sign is -1 or 1 (#1345, #1507) * Documentation on how to write 1D and 2D controllers (#1494) * Missing documentation of SEP18 concepts to how-to counter/timer controller (#995, #1492) From 9c23e45907d5822da49687912c797eebb63a8451 Mon Sep 17 00:00:00 2001 From: Jose Tiago Macara Coutinho Date: Thu, 10 Oct 2019 12:13:37 +0200 Subject: [PATCH 81/82] Fix Tango inheritance --- src/sardana/tango/core/SardanaDevice.py | 8 ++++---- src/sardana/tango/pool/Pool.py | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sardana/tango/core/SardanaDevice.py b/src/sardana/tango/core/SardanaDevice.py index 3e496be7a2..7d8d9ca70c 100644 --- a/src/sardana/tango/core/SardanaDevice.py +++ b/src/sardana/tango/core/SardanaDevice.py @@ -35,7 +35,7 @@ import threading import PyTango.constants -from PyTango import Device_4Impl, DeviceClass, Util, DevState, \ +from PyTango import LatestDeviceImpl, DeviceClass, Util, DevState, \ AttrQuality, TimeVal, ArgType, ApiUtil, DevFailed, WAttribute from taurus.core.util.threadpool import ThreadPool @@ -66,7 +66,7 @@ def get_thread_pool(): return __thread_pool -class SardanaDevice(Device_4Impl, Logger): +class SardanaDevice(LatestDeviceImpl, Logger): """SardanaDevice represents the base class for all Sardana :class:`PyTango.DeviceImpl` classes""" @@ -74,7 +74,7 @@ def __init__(self, dclass, name): """Constructor""" self.in_constructor = True try: - Device_4Impl.__init__(self, dclass, name) + LatestDeviceImpl.__init__(self, dclass, name) self.init(name) Logger.__init__(self, name) @@ -512,7 +512,7 @@ def _get_class_properties(self): return dict(ProjectTitle="Sardana", Description="Generic description", doc_url="http://sardana-controls.org/", __icon=self.get_name().lower() + ".png", - InheritedFrom=["Device_4Impl"]) + InheritedFrom=["Device_5Impl"]) def write_class_property(self): """Write class properties ``ProjectTitle``, ``Description``, diff --git a/src/sardana/tango/pool/Pool.py b/src/sardana/tango/pool/Pool.py index e073abf4f4..54c0e29934 100644 --- a/src/sardana/tango/pool/Pool.py +++ b/src/sardana/tango/pool/Pool.py @@ -48,12 +48,12 @@ import collections -class Pool(PyTango.Device_4Impl, Logger): +class Pool(PyTango.LatestDeviceImpl, Logger): ElementsCache = None def __init__(self, cl, name): - PyTango.Device_4Impl.__init__(self, cl, name) + PyTango.LatestDeviceImpl.__init__(self, cl, name) Logger.__init__(self, name) self.init(name) self.init_device() @@ -1576,7 +1576,7 @@ def __init__(self, name): def _get_class_properties(self): return dict(ProjectTitle="Sardana", Description="Device Pool management class", doc_url="http://sardana-controls.org/", - InheritedFrom="Device_4Impl") + InheritedFrom="Device_5Impl") def write_class_property(self): util = PyTango.Util.instance() From 34bd40be3d316c5d6f0a88ac88ef9001c36a0143 Mon Sep 17 00:00:00 2001 From: reszelaz Date: Thu, 18 Mar 2021 21:35:26 +0100 Subject: [PATCH 82/82] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11d03bb5b0..4dc90b78dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,6 +63,8 @@ This file follows the formats and conventions from [keepachangelog.com] * Experimental channel shape is now considered as a result of the configuration and not part of the measurement group configuration (#1296, #1466) +* Use `LatestDeviceImpl` (currently `Device_5Impl`) for as a base class of the Sardana Tango + devices (#1214, #1301, #1531) ### Removed