Abo

The abo subpackage provides modules with various ways and classes for higher level synchronous readout from the DOOCS control system. The contents of this subpackage is presented below:

import doocspie


help(doocspie.abo)
Help on package doocspie.abo in doocspie:

NAME
    doocspie.abo - Modules for higher level synchronous readout from DOOCS.

PACKAGE CONTENTS
    synchronizer
    train_abo
    train_event

FILE
    /home/cbehrens/Home/Repositories/gitlab/doocspie/doocspie/abo/__init__.py


In the following, we show how to use instances of TrainAbo to synchronously acquire instances of TrainEvent, which contains all the relevant data of interest. The synchronization backend, which is provided by the Synchronizer, is described as well. The latter can also be used independently.

TrainAbo

The simplest and most convenient way to synchronously retrieve data can be realized with the TrainAbo class of the train_abo module, and its entire documentation is presented here:

help(doocspie.abo.train_abo.TrainAbo)
Help on class TrainAbo in module doocspie.abo.train_abo:

class TrainAbo(builtins.object)
 |  TrainAbo(properties=None, train_events=None, timeout_seconds=10, buffer_size=32, allow_zero_events=False)
 |  
 |  TrainAbo class for higher level synchronous readout from DOOCS.
 |  
 |  This class provides the train abo for higher level synchronous readout from DOOCS.
 |  
 |  Methods defined here:
 |  
 |  __init__(self, properties=None, train_events=None, timeout_seconds=10, buffer_size=32, allow_zero_events=False)
 |      Constructor of the train abo class.
 |      
 |      This constructor initializes the instance with optional parameters (see 'Args' below).
 |      
 |      Args:
 |          properties (tuple or dict, optional): The optional properties to get train events with readouts for.
 |          train_events (int, optional): The optional number of train events to synchronously readout.
 |          timeout_seconds (int): The optional timeout seconds for unsuccessful readout.
 |          buffer_size (int): The optional number of buffers to be used for storing previous readouts.
 |          allow_zero_events (bool, optional): The optional state for allow zero train events.
 |  
 |  __iter__(self)
 |      Special method to provide a train abo iterator.
 |  
 |  __next__(self)
 |      Special method to provide the next train event for the train abo iterator.
 |  
 |  __str__(self)
 |      Special method to return a properly formatted string representation of the train abo.
 |  
 |  add(self, address, label=None, offset=None, timestamp_event=None, meta_event=None, start=None, elements=None)
 |      Add address with optional parameters to the properties for synchronous readout.
 |      
 |      Args:
 |          address (str): The DOOCS address to add to the properties for synchronous readout.
 |          label (str, optional): Optional label for DOOCS address as a means for alternative property access.
 |          offset (int, optional): Optional offset to be applied on event numbers for synchronization.
 |          timestamp_event (bool, optional): Optional state to determine using the timestamp as an alternative event.
 |          meta_event (str, optional): Optional Meta property to replace the default event with.
 |          start (int, optional): Optional start position for reading out array-like types.
 |          elements (int, optional): Optional number of elements to read out for array-like types.
 |      
 |      Returns:
 |          None
 |      
 |      Raises:
 |          DoocspieException: Doocspie related exception for address duplication.
 |  
 |  reset(self)
 |      Reset the train abo in order to use it again.
 |      
 |      Returns:
 |          None
 |  
 |  start_loop(self, function)
 |      Start the loop the train abo and execute the given function once there is a new train event.
 |      
 |      Args:
 |          function (function): The function to execute for a new train event.
 |      
 |      Returns:
 |          None
 |  
 |  stop_loop(self)
 |      Stop the loop of the train abo and its execution of the supplied function.
 |      
 |      Returns:
 |          None
 |  
 |  update(self)
 |      Update the newly added properties for synchronous readout.
 |      
 |      Returns:
 |          None
 |  
 |  ----------------------------------------------------------------------
 |  Readonly properties defined here:
 |  
 |  actual_properties
 |      tuple: The properties that can actually be readout and returned via TrainEvent's 'get'.
 |  
 |  train_event
 |      int: The number of the current train event in the train abo.
 |  
 |  train_events
 |      int: The number of total train events in the train abo.
 |  
 |  unusable_properties
 |      tuple: The properties that cannot be used for synchronized readout in the train abo.
 |  
 |  usable_properties
 |      tuple: The properties that can be used for synchronized readout in the train abo.
 |  
 |  zero_event_properties
 |      tuple: The properties that have an event number of 'zero'.
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)

The TrainAbo class can simply be imported directly from the doocspie.abo subpackage as shown in the code example below. Here, we instantiate an empty instance and print out its content.

from doocspie.abo import TrainAbo

train_abo = TrainAbo()

print(train_abo)
TrainAbo(train_events=None, train_event=0, actual_properties=())

The printout above reveals the important attributes of the TrainAbo instance, namely the number of total train_events to return (None corresponds to unlimited events), the current train_event number and the properties that are actually be recorded, called actual_properties. No properties were added to the TrainAbo instance for recording yet, but this will be carried out in the following:

train_abo.add("TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/FLOAT.BUFFER", label="foo")
train_abo.add("TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/INTEGER.BUFFER", offset=0)
print(train_abo)

train_abo.update()
print(train_abo)

train_abo.add("TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/SPECTRUM", start=10, elements=20)
print(train_abo)
TrainAbo(train_events=None, train_event=0, actual_properties=())
TrainAbo(train_events=None, train_event=0, actual_properties=('TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/FLOAT.BUFFER', 'foo', 'TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/INTEGER.BUFFER'))
TrainAbo(train_events=None, train_event=0, actual_properties=('TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/FLOAT.BUFFER', 'foo', 'TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/INTEGER.BUFFER'))

While adding DOOCS properties for synchronous readout via the add method, the two optional parameters label and offset can be supplied among others. The label parameter provides an alternative, more convenient way to access the data, and the offset parameter can be applied for synchronization when unintentional offsets in the event number exist. The parameters start and elements additionally allow to read out only chunks of the entire array data for array-like DOOCS types, e.g. SPECTRUM, when supported by the server. The TrainAbo does internal feasibility checks on the added properties, hence the update method must be called in order to see effects on the added properties (see example above). However, this update method is also being called implicitly when data is being recorded, hence it can usually be omitted. The following code lines demonstrate how to actually get data from the TrainAbo instance, making use of its iterator protocol:

train_event = next(train_abo)

print(train_event)
TrainEvent(id=3524121, properties=('TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/FLOAT.BUFFER', 'foo', 'TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/INTEGER.BUFFER'))

The acquired data provided by TrainAbo is aggregated in an instance of the TrainEvent, having an id that represents the event number of all simultaneously recorded DOOCS properties and the actually recorded properties themselves. In the example above, the two previously added DOOCS properties, accessible either via their addresses or supplied labels, are included in the TrainEvent instance. The TrainEvent class is described more carefully in a separated section below.

An alternative way to add properties to the TrainAbo is by supplying the DOOCS addresses with the optional label, offset, start, elements, timestamp_event and/or meta_event key-value pairs during instantiation, which is demonstrated in the following code example. Here, the three update invocation mentioned above is not required. The properties (attributes) usable_properties, unusable_properties and actual_properties are also being introduced, which are convenient for debugging purposes. A few options are also being used in an example at the end of this section.

addresses = {"TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/FLOAT.BUFFER": {"label": "float",
                                                                    "offset": 1},
             "TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/SPECTRUM": {"start": 100,
                                                                "elements": 200},
             "TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/INTEGER.BUFFER": {"label": "integer"}}

train_abo = TrainAbo(addresses)

print("usable properties:", train_abo.usable_properties)
print("unusable properties:", train_abo.unusable_properties)
print("actual properties:", train_abo.actual_properties)
usable properties: (Usable(address='TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/FLOAT.BUFFER', label='float', offset=1, timestamp_event=None, meta_event=None, start=None, elements=None), Usable(address='TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/INTEGER.BUFFER', label='integer', offset=None, timestamp_event=None, meta_event=None, start=None, elements=None))
unusable properties: (Unusable(address='TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/SPECTRUM', label=None, reason='zero event number'),)
actual properties: ('TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/FLOAT.BUFFER', 'float', 'TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/INTEGER.BUFFER', 'integer')

The DOOCS properties in the TrainAbo are fundamentally being split into usable_properties and unusable_properties, where the latter cannot be used for synchronized readout for a number of reasons. The actual reason is also given in the corresponding property output, and in the previous example, it is due to zero event numbers (e.g. typical for actuators or slowly changing values in general). In some situations, one might want to record ‘zero events’ anyway, and this can be realized by the optional allow_zero_events parameter. Those DOOCS properties are then still listed in unusable_properties, but they also appear in actual_properties, which determines the properties that get actually recorded in a TrainEvent. This is presented in the two following examples:

addresses = ("TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/FLOAT.BUFFER",
             "TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/FLOAT")

train_abo = TrainAbo(addresses, allow_zero_events=True)

print("usable properties:", train_abo.usable_properties)
print("unusable properties:", train_abo.unusable_properties)
print("actual properties:", train_abo.actual_properties)
usable properties: (Usable(address='TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/FLOAT.BUFFER', label=None, offset=None, timestamp_event=None, meta_event=None, start=None, elements=None),)
unusable properties: (Unusable(address='TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/FLOAT', label=None, reason='zero event number'),)
actual properties: ('TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/FLOAT.BUFFER', 'TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/FLOAT')
train_event = next(train_abo)

print(train_event)
TrainEvent(id=3524131, properties=('TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/FLOAT.BUFFER', 'TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/FLOAT'))

The TrainAbo class implements the iterator protocol and thus allows convenient iteration in a simple for loop as is demonstrated in the following. The TrainAbo instance being used has been instantiated without a certain number of train_events, hence a break condition is being applied in order to prevent unlimited iteration. The subsequent example then presents how to provide a train_events parameter to limit the iteration, and the attributes train_event(s) are introduced.

for index, train_event in enumerate(train_abo):
    if index == 3:
        break
    print("train event:", train_event.id)
train event: 3524132
train event: 3524133
train event: 3524134
addresses = ("TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/FLOAT.BUFFER",
             "TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/INTEGER.BUFFER")

train_abo = TrainAbo(addresses, train_events=3)

print("train event / train events:", train_abo.train_event, "of", train_abo.train_events, "\n")
for train_event in train_abo:
    print("train event:", train_event.id)
train event / train events: 0 of 3

train event: 3524140
train event: 3524141
train event: 3524142

Once all train_events have been generated, the TrainAbo instance is exhausted as is shown here:

print("train event / train events:", train_abo.train_event, "of", train_abo.train_events, "\n")
for train_event in train_abo:
    print("train event:", train_event.id)  # not being executed

try:
    next(train_abo)
except StopIteration as err:
    print(err)
train event / train events: 3 of 3

all train events have been generated

In order to use it again, one can either create a new instance or apply the reset method like that:

train_abo.reset()  # reset in order to use it again

print("train event / train events:", train_abo.train_event, "of", train_abo.train_events, "\n")
for train_event in train_abo:
    print("train event:", train_event.id)
train event / train events: 0 of 3

train event: 3524143
train event: 3524144
train event: 3524145

Besides iterators, the TrainAbo supports callback functions that get provided with TrainEvent instances. In the following example, we re-use the previous TrainAbo instance by resetting it, and define a callback function that gets executed in a loop by passing it to the start_loop method. The callback function can be named anything, and its one parameter (name ist also arbitrary) will be an instance of TrainEvent. The callback function’s name without parentheses must be supplied.

train_abo.reset()

def callback(train_event):
    print("train event:", train_event.id)

train_abo.start_loop(callback)
train event: 3524146
train event: 3524147
train event: 3524148

In case the TrainAbo is being instantiated without a certain number of train_events, the loop that is executing the callback function can be stopped via the stop_loop methods as is shown here:

addresses = ("TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/FLOAT.BUFFER",
             "TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/INTEGER.BUFFER")

train_abo = TrainAbo(addresses)

counter = 0
def callback(train_event):
    global counter
    counter += 1
    print("train event:", train_event.id)
    if counter == 3:
        train_abo.stop_loop()

train_abo.start_loop(callback)
train event: 3524153
train event: 3524154
train event: 3524155

Finally, we demonstrate both the iterator and callback function approach of the TrainAbo class for synchronized readout in a typical use case, respectively. We instantiate a TrainAbo instance with fifty train_events and two DOOCS addresses for charge monitors (toroids), which are supposed to be correlated to a very high degree. We then collect the charge of the first bunch of each charge monitor (using the [0] indexing) in a list and eventually plot them against each other.

import matplotlib.pyplot as plt

train_abo = TrainAbo(train_events=50)

train_abo.add("FLASH.DIAG/TOROID/1FL0UBC2/CHARGE.TD", label="charge 1")
train_abo.add("FLASH.DIAG/TOROID/2FL0DBC2/CHARGE.TD", label="charge 2")

charge_1 = list()
charge_2 = list()

for train_event in train_abo:
    charge_1.append(train_event.get("charge 1").data[0])
    charge_2.append(train_event.get("charge 2").data[0])

plt.plot(charge_1, charge_2, "o")
plt.xlabel("Charge 1 (nC)")
plt.ylabel("Charge 2 (nC)");
../_images/abo_14_0.png

Here, we simply re-use the previous TrainAbo instance by resetting it and clearing the lists:

train_abo.reset()

charge_1.clear()
charge_2.clear()

def correlate_charge(train_event):
    charge_1.append(train_event.get("charge 1").data[0])
    charge_2.append(train_event.get("charge 2").data[0])

train_abo.start_loop(correlate_charge)

plt.plot(charge_1, charge_2, "o")
plt.xlabel("Charge 1 (nC)")
plt.ylabel("Charge 2 (nC)");
../_images/abo_15_0.png

The two parameters timeout_seconds and buffer_size most likely do not have to be touched ever, because they affect the internals of the synchronization backend and are adjusted properly.

In the following two code blocks, we demonstrate how configure the TrainAbo with the both options start and elements in order to read out only a limited range of data from a SPECTRUM type. First we plot the entire array, showing the bunch charge as a function of time separation, i.e. the charge along the three electron beamlines at FLASH. Then we use a TrainAbo instance and read out only a certain number of elements starting at a certain position and plot the result.

readout = doocspie.get("FLASH_SIM.DIAG/TOROID/3GUN/CHARGE.TD")

plt.plot(doocspie.util.get_time_axis(readout), readout.data)
plt.xlabel("Time (\u03bcs)")
plt.ylabel("Charge (nC)");
../_images/abo_16_0.png
addresses = {"FLASH_SIM.DIAG/TOROID/3GUN/CHARGE.TD": {"label": "charge",
                                                      "start": 875,
                                                      "elements": 200}}

train_abo = TrainAbo(addresses)
train_event = next(train_abo)

readout = train_event.get("charge")

plt.plot(doocspie.util.get_time_axis(readout), readout.data)
plt.xlabel("Time (\u03bcs)")
plt.ylabel("Charge (nC)");
../_images/abo_17_0.png

The latter plot effectively presents the bunch charge along the FLASH2 electron beamline.

If the DOOCS properties of a particular server do not provide an appropriate event number, i.e. it is either 0 or static, the direct timestamp attribute or a property from the meta attribute can serve as an alternative event number for the TrainAbo instance. As similar to the start and elements options utilized in the previous code example, the alternative events can be applied via the boolean timestamp_event or the meta_event string, either in the configuration dict or by using the add method with the timestamp_event and the meta_event parameter. Possible values for these two parameters (options) are shown in documentation about the get method of doocspie.io.

TrainEvent

The previous section already dealt with instances of TrainEvent, and here we will cover it in a more detailed manner. Its documentation, including the most relevant method get, is given below:

help(doocspie.abo.train_abo.TrainEvent)
Help on class TrainEvent in module doocspie.abo.train_event:

class TrainEvent(builtins.object)
 |  TrainEvent(readouts, properties)
 |  
 |  TrainEvent class for higher level synchronous readout from DOOCS.
 |  
 |  This class provides the train event for higher level synchronous readout from DOOCS.
 |  
 |  Methods defined here:
 |  
 |  __init__(self, readouts, properties)
 |      Constructor of the train event class.
 |      
 |      This constructor initializes the instance using the readouts and its related properties.
 |      
 |      Args:
 |          readouts (dict): The readouts for the related properties.
 |          properties (tuple): The properties related to the readouts.
 |  
 |  __str__(self)
 |      Special method to return a properly formatted string representation of the train event.
 |  
 |  get(self, property)
 |      Get the readout from the train event for the given property.
 |      
 |      Args:
 |          property (str): The property for the readout to get from the train event.
 |      
 |      Returns:
 |          Readout: An instance of the readout object for the requested property.
 |      
 |      Raises:
 |          DoocspieException: Doocspie related exception for non-existing property requested.
 |  
 |  ----------------------------------------------------------------------
 |  Readonly properties defined here:
 |  
 |  id
 |      int: The id of the current train event.
 |  
 |  properties
 |      tuple: The properties that can be returned via 'get'.
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)

As already carried out multiple times in the earlier section, we create a TrainAbo instance and add two DOOCS properties including a label, one during instantiation and the other by means of the add method. We do this simply as a reminder of how adding properties can be realized. Once the TrainAbo instance is set up, we call next to get a TrainEvent instance, which gets printed:

addresses = {"TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/FLOAT.BUFFER": {"label": "float"}}

train_abo = TrainAbo(addresses)
train_abo.add("TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/INTEGER.BUFFER", label="integer")

train_event = next(train_abo)
print(train_event)
TrainEvent(id=3524281, properties=('TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/FLOAT.BUFFER', 'float', 'TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/INTEGER.BUFFER', 'integer'))

The TrainEvent instance contains the recorded DOOCS properties, i.e. the address and optionally the corresponding label, in its properties attribute, and those are presented in the following:

for _property in train_event.properties:
    print("property:", _property)
property: TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/FLOAT.BUFFER
property: float
property: TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/INTEGER.BUFFER
property: integer

The actual data that is recorded synchronously can be retrieved via the get method, which returns a Readout instance (the standard way of representing data in doocspie), as is demonstrated below:

print("TrainEvent's 'get' method returns a 'Readout':",
      all(isinstance(train_event.get(_property), doocspie.doocspie.io.Readout)
          for _property in train_event.properties))
TrainEvent's 'get' method returns a 'Readout': True

The id attribute of the TrainEvent instance represents the event number that all synchronously recorded DOOCS properties share simultaneously. This is shown in the following code example by comparing the id with the event of Readout that is returned via get for each property:

print("   train event:", train_event.id)
for _property in train_event.properties:
    print("property event:", train_event.get(_property).event)
   train event: 3524281
property event: 3524281
property event: 3524281
property event: 3524281
property event: 3524281

Finally, we present the DoocspieException message for not existing properties in the TrainEvent:

try:
    train_event.get("not existing")
except doocspie.DoocspieException as exc:
    print("exception message:", exc.message)
exception message: readout for 'not existing' does not exist

Synchronizer

The synchronization backend of the TrainAbo above is provided by the Synchronizer class. This class provides an interface that is very similar to the TrainAbo and is presented below:

help(doocspie.abo.train_abo.Synchronizer)
Help on class Synchronizer in module doocspie.abo.synchronizer:

class Synchronizer(builtins.object)
 |  Synchronizer(properties=None, timeout_seconds=10, buffer_size=32, allow_zero_events=False)
 |  
 |  Synchronizer class for higher level synchronous readout from DOOCS.
 |  
 |  This class provides the synchronizer for higher level synchronous readout from DOOCS.
 |  
 |  Methods defined here:
 |  
 |  __init__(self, properties=None, timeout_seconds=10, buffer_size=32, allow_zero_events=False)
 |      Constructor of the synchronizer class.
 |      
 |      This constructor initializes the instance with optional parameters (see 'Args' below).
 |      
 |      Args:
 |          properties (tuple or dict, optional): The optional properties to get train events with readouts for.
 |          timeout_seconds (int): The optional timeout seconds for unsuccessful readout.
 |          buffer_size (int): The optional number of buffers to be used for storing previous readouts.
 |          allow_zero_events (bool, optional): The optional state for allow zero train events.
 |  
 |  __str__(self)
 |      Special method to return a properly formatted string representation of the synchronizer.
 |  
 |  add(self, address, label=None, offset=None, timestamp_event=None, meta_event=None, start=None, elements=None)
 |      Add address with optional parameters to the properties for synchronous readout.
 |      
 |      Args:
 |          address (str): The DOOCS address to add to the properties for synchronous readout.
 |          label (str, optional): Optional label for DOOCS address as a means for alternative property access.
 |          offset (int, optional): Optional offset to be applied on event numbers for synchronization.
 |          timestamp_event (bool, optional): Optional state to determine using the timestamp as an alternative event.
 |          meta_event (str, optional): Optional Meta property to replace the default event with.
 |          start (int, optional): Optional start position for reading out array-like types.
 |          elements (int, optional): Optional number of elements to read out for array-like types.
 |      
 |      Returns:
 |          None
 |      
 |      Raises:
 |          DoocspieException: Doocspie related exception for address duplication.
 |  
 |  get(self)
 |      Get the synchronous readouts mapped by its address or label for the particular properties.
 |      
 |      Returns:
 |          dict: The address (key) and readout (value) for the particular properties.
 |      
 |      Raises:
 |          DoocspieException: Doocspie related exception for synchronization timeout or no usable properties.
 |  
 |  get_event_matched_readouts(self)
 |      Get the event matched readouts mapped by its address or label for the particular properties.
 |      
 |      Returns:
 |          dict: The address (key) and readout (value) for the particular properties.
 |  
 |  get_label_of(self, source)
 |      Get the optional label of the given source.
 |      
 |      Args:
 |          source (str): The source to get the label for.
 |      
 |      Returns:
 |          str: The optional label of the given source.
 |  
 |  update(self)
 |      Update the newly added properties for synchronous readout.
 |      
 |      Returns:
 |          None
 |  
 |  ----------------------------------------------------------------------
 |  Readonly properties defined here:
 |  
 |  actual_properties
 |      tuple: The properties that can actually be readout.
 |  
 |  timeout_seconds
 |      int: The timeout seconds for unsuccessful readout.
 |  
 |  unusable_properties
 |      tuple: The properties that cannot be used for synchronized readout in the train abo.
 |  
 |  usable_properties
 |      tuple: The properties that can be used for synchronized readout in the train abo.
 |  
 |  zero_event_properties
 |      tuple: The properties that have an event number of 'zero'.
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)

In the earlier sections above, the features of the TrainAbo have been demonstrated already. We further highlight the capabilities of the synchronized readout by instantiating a Synchronizer with a list of all charge and beam position monitors at FLASH in the following code example:

from doocspie.abo import Synchronizer

addresses = [location + "/CHARGE.TD" for location in doocspie.ls("FLASH.DIAG/TOROID/*")]
addresses.extend([location + "/X.ALL" for location in doocspie.ls("FLASH.DIAG/BPM/*")])

synchronizer = Synchronizer(addresses)
print(synchronizer)
Synchronizer(timeout_seconds=10, buffer_size=32, allow_zero_events=False, actual_properties=('FLASH.DIAG/TOROID/1FL0UBC2/CHARGE.TD', 'FLASH.DIAG/TOROID/2FL0DBC2/CHARGE.TD', 'FLASH.DIAG/TOROID/3GUN/CHARGE.TD', 'FLASH.DIAG/TOROID/2FL0UBC1/CHARGE.TD', 'FLASH.DIAG/TOROID/8FL0DBC1/CHARGE.TD', 'FLASH.DIAG/TOROID/18ACC7/CHARGE.TD', 'FLASH.DIAG/TOROID/4TCOL/CHARGE.TD', 'FLASH.DIAG/TOROID/1ORS/CHARGE.TD', 'FLASH.DIAG/TOROID/4FL2EXTR/CHARGE.TD', 'FLASH.DIAG/TOROID/18FL2EXTR/CHARGE.TD', 'FLASH.DIAG/TOROID/1FL2SEED4/CHARGE.TD', 'FLASH.DIAG/TOROID/5FLFEXTR/CHARGE.TD', 'FLASH.DIAG/TOROID/7FLFMAFF/CHARGE.TD', 'FLASH.DIAG/TOROID/7FL2XTDS/CHARGE.TD', 'FLASH.DIAG/TOROID/7FL2DUMP/CHARGE.TD', 'FLASH.DIAG/TOROID/7FLFDUMP/CHARGE.TD', 'FLASH.DIAG/TOROID/2SDUMP/CHARGE.TD', 'FLASH.DIAG/TOROID/11SMATCH/CHARGE.TD', 'FLASH.DIAG/TOROID/12EXP/CHARGE.TD', 'FLASH.DIAG/TOROID/9DUMP/CHARGE.TD', 'FLASH.DIAG/BPM/1GUN/X.ALL', 'FLASH.DIAG/BPM/3GUN/X.ALL', 'FLASH.DIAG/BPM/2FL0UBC1/X.ALL', 'FLASH.DIAG/BPM/9ACC1/X.ALL', 'FLASH.DIAG/BPM/5FL0LAHE/X.ALL', 'FLASH.DIAG/BPM/1FL0DBC1/X.ALL', 'FLASH.DIAG/BPM/3FL0DBC1/X.ALL', 'FLASH.DIAG/BPM/5FL0DBC1/X.ALL', 'FLASH.DIAG/BPM/7FL0DBC1/X.ALL', 'FLASH.DIAG/BPM/10ACC2/X.ALL', 'FLASH.DIAG/BPM/10ACC3/X.ALL', 'FLASH.DIAG/BPM/2FL0UBC2/X.ALL', 'FLASH.DIAG/BPM/2FL0CBC2/X.ALL', 'FLASH.DIAG/BPM/3FL0CBC2/X.ALL', 'FLASH.DIAG/BPM/1FL0DBC2/X.ALL', 'FLASH.DIAG/BPM/3FL0DBC2/X.ALL', 'FLASH.DIAG/BPM/5FL0DBC2/X.ALL', 'FLASH.DIAG/BPM/9ACC4/X.ALL', 'FLASH.DIAG/BPM/9ACC5/X.ALL', 'FLASH.DIAG/BPM/9ACC6/X.ALL', 'FLASH.DIAG/BPM/11ACC7/X.ALL', 'FLASH.DIAG/BPM/8TCOL/X.ALL', 'FLASH.DIAG/BPM/3ECOL/X.ALL', 'FLASH.DIAG/BPM/5ECOL/X.ALL', 'FLASH.DIAG/BPM/2ORS/X.ALL', 'FLASH.DIAG/BPM/4ORS/X.ALL', 'FLASH.DIAG/BPM/7ORS/X.ALL', 'FLASH.DIAG/BPM/9ORS/X.ALL', 'FLASH.DIAG/BPM/12ORS/X.ALL', 'FLASH.DIAG/BPM/1SFUND2/X.ALL', 'FLASH.DIAG/BPM/1SFUND3/X.ALL', 'FLASH.DIAG/BPM/1SFUND4/X.ALL', 'FLASH.DIAG/BPM/1SFELC/X.ALL', 'FLASH.DIAG/BPM/1SMATCH/X.ALL', 'FLASH.DIAG/BPM/6SMATCH/X.ALL', 'FLASH.DIAG/BPM/13SMATCH/X.ALL', 'FLASH.DIAG/BPM/14SMATCH/X.ALL', 'FLASH.DIAG/BPM/5UND1/X.ALL', 'FLASH.DIAG/BPM/5UND2/X.ALL', 'FLASH.DIAG/BPM/5UND3/X.ALL', 'FLASH.DIAG/BPM/5UND4/X.ALL', 'FLASH.DIAG/BPM/5UND5/X.ALL', 'FLASH.DIAG/BPM/5UND6/X.ALL', 'FLASH.DIAG/BPM/3EXP/X.ALL', 'FLASH.DIAG/BPM/9EXP/X.ALL', 'FLASH.DIAG/BPM/9DUMP/X.ALL', 'FLASH.DIAG/BPM/10DUMP/X.ALL', 'FLASH.DIAG/BPM/13DUMP/X.ALL', 'FLASH.DIAG/BPM/15ACC7/X.ALL', 'FLASH.DIAG/BPM/19ACC7/X.ALL', 'FLASH.DIAG/BPM/1TCOL/X.ALL', 'FLASH.DIAG/BPM/6TCOL/X.ALL', 'FLASH.DIAG/BPM/4FL2EXTR/X.ALL', 'FLASH.DIAG/BPM/5FL2EXTR/X.ALL', 'FLASH.DIAG/BPM/8FL2EXTR/X.ALL', 'FLASH.DIAG/BPM/11FL2EXTR/X.ALL', 'FLASH.DIAG/BPM/13FL2EXTR/X.ALL', 'FLASH.DIAG/BPM/15FL2EXTR/X.ALL', 'FLASH.DIAG/BPM/18FL2EXTR/X.ALL', 'FLASH.DIAG/BPM/21FL2EXTR/X.ALL', 'FLASH.DIAG/BPM/22FL2EXTR/X.ALL', 'FLASH.DIAG/BPM/1FL2BC/X.ALL', 'FLASH.DIAG/BPM/6FL2BC/X.ALL', 'FLASH.DIAG/BPM/3FL2SEED3/X.ALL', 'FLASH.DIAG/BPM/3FL2SEED4/X.ALL', 'FLASH.DIAG/BPM/3FL2SEED5/X.ALL', 'FLASH.DIAG/BPM/3FL2SEED6/X.ALL', 'FLASH.DIAG/BPM/3FL2SASE2/X.ALL', 'FLASH.DIAG/BPM/3FL2SASE3/X.ALL', 'FLASH.DIAG/BPM/3FL2SASE4/X.ALL', 'FLASH.DIAG/BPM/3FL2SASE5/X.ALL', 'FLASH.DIAG/BPM/3FL2SASE7/X.ALL', 'FLASH.DIAG/BPM/3FL2SASE8/X.ALL', 'FLASH.DIAG/BPM/3FL2SASE9/X.ALL', 'FLASH.DIAG/BPM/3FL2SASE10/X.ALL', 'FLASH.DIAG/BPM/3FL2SASE11/X.ALL', 'FLASH.DIAG/BPM/3FL2SASE12/X.ALL', 'FLASH.DIAG/BPM/3FL2SASE13/X.ALL', 'FLASH.DIAG/BPM/3FL2BURN/X.ALL', 'FLASH.DIAG/BPM/3FL2XTDS/X.ALL', 'FLASH.DIAG/BPM/9FL2XTDS/X.ALL', 'FLASH.DIAG/BPM/5FL2DUMP/X.ALL', 'FLASH.DIAG/BPM/6FL2DUMP/X.ALL', 'FLASH.DIAG/BPM/10FL2DUMP/X.ALL'))

In contrast to the TrainAbo, the Synchronizer provides a parameterless get method that returns a Python dict with all simultaneously recorded property addresses and Readout instances. The current number of recorded readout items and the readout’s dictionary itself is printed here:

readouts = synchronizer.get()

print("number of readouts:", len(readouts))
print("readout dictionary:", readouts)
number of readouts: 104
readout dictionary: {'FLASH.DIAG/TOROID/1FL0UBC2/CHARGE.TD': <doocspie.doocspie.readout.Readout object at 0x7f3f32f207f0>, 'FLASH.DIAG/TOROID/2FL0DBC2/CHARGE.TD': <doocspie.doocspie.readout.Readout object at 0x7f3f32f203a0>, 'FLASH.DIAG/TOROID/3GUN/CHARGE.TD': <doocspie.doocspie.readout.Readout object at 0x7f3f32f21030>, 'FLASH.DIAG/TOROID/2FL0UBC1/CHARGE.TD': <doocspie.doocspie.readout.Readout object at 0x7f3f32f22530>, 'FLASH.DIAG/TOROID/8FL0DBC1/CHARGE.TD': <doocspie.doocspie.readout.Readout object at 0x7f3f32f20e50>, 'FLASH.DIAG/TOROID/18ACC7/CHARGE.TD': <doocspie.doocspie.readout.Readout object at 0x7f3f32f21150>, 'FLASH.DIAG/TOROID/4TCOL/CHARGE.TD': <doocspie.doocspie.readout.Readout object at 0x7f3f32f204f0>, 'FLASH.DIAG/TOROID/1ORS/CHARGE.TD': <doocspie.doocspie.readout.Readout object at 0x7f3f32f21750>, 'FLASH.DIAG/TOROID/4FL2EXTR/CHARGE.TD': <doocspie.doocspie.readout.Readout object at 0x7f3f32f21360>, 'FLASH.DIAG/TOROID/18FL2EXTR/CHARGE.TD': <doocspie.doocspie.readout.Readout object at 0x7f3f32f205b0>, 'FLASH.DIAG/TOROID/1FL2SEED4/CHARGE.TD': <doocspie.doocspie.readout.Readout object at 0x7f3f32f22380>, 'FLASH.DIAG/TOROID/5FLFEXTR/CHARGE.TD': <doocspie.doocspie.readout.Readout object at 0x7f3f32f213f0>, 'FLASH.DIAG/TOROID/7FLFMAFF/CHARGE.TD': <doocspie.doocspie.readout.Readout object at 0x7f3f32f21b70>, 'FLASH.DIAG/TOROID/7FL2XTDS/CHARGE.TD': <doocspie.doocspie.readout.Readout object at 0x7f3f32f21e40>, 'FLASH.DIAG/TOROID/7FL2DUMP/CHARGE.TD': <doocspie.doocspie.readout.Readout object at 0x7f3f32f21f90>, 'FLASH.DIAG/TOROID/7FLFDUMP/CHARGE.TD': <doocspie.doocspie.readout.Readout object at 0x7f3f32f21a50>, 'FLASH.DIAG/TOROID/2SDUMP/CHARGE.TD': <doocspie.doocspie.readout.Readout object at 0x7f3f32f22050>, 'FLASH.DIAG/TOROID/11SMATCH/CHARGE.TD': <doocspie.doocspie.readout.Readout object at 0x7f3f32f21d80>, 'FLASH.DIAG/TOROID/12EXP/CHARGE.TD': <doocspie.doocspie.readout.Readout object at 0x7f3f32f21cc0>, 'FLASH.DIAG/TOROID/9DUMP/CHARGE.TD': <doocspie.doocspie.readout.Readout object at 0x7f3f32f21870>, 'FLASH.DIAG/BPM/1GUN/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f32f226e0>, 'FLASH.DIAG/BPM/3GUN/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f32f228f0>, 'FLASH.DIAG/BPM/2FL0UBC1/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f32f225f0>, 'FLASH.DIAG/BPM/9ACC1/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f32f22cb0>, 'FLASH.DIAG/BPM/5FL0LAHE/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f32f223e0>, 'FLASH.DIAG/BPM/1FL0DBC1/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f32f22b00>, 'FLASH.DIAG/BPM/3FL0DBC1/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f32f222f0>, 'FLASH.DIAG/BPM/5FL0DBC1/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f32f21ae0>, 'FLASH.DIAG/BPM/7FL0DBC1/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f32f229b0>, 'FLASH.DIAG/BPM/10ACC2/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f32f22320>, 'FLASH.DIAG/BPM/10ACC3/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f32f230d0>, 'FLASH.DIAG/BPM/2FL0UBC2/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f32f23070>, 'FLASH.DIAG/BPM/2FL0CBC2/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f32f22f80>, 'FLASH.DIAG/BPM/3FL0CBC2/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f32f237c0>, 'FLASH.DIAG/BPM/1FL0DBC2/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f32f22fb0>, 'FLASH.DIAG/BPM/3FL0DBC2/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f32f23610>, 'FLASH.DIAG/BPM/5FL0DBC2/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f32f22ec0>, 'FLASH.DIAG/BPM/9ACC4/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f32f23460>, 'FLASH.DIAG/BPM/9ACC5/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f32f22dd0>, 'FLASH.DIAG/BPM/9ACC6/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3a4074c0>, 'FLASH.DIAG/BPM/11ACC7/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3a407f70>, 'FLASH.DIAG/BPM/8TCOL/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3a406f50>, 'FLASH.DIAG/BPM/3ECOL/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3a407250>, 'FLASH.DIAG/BPM/5ECOL/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3a406e60>, 'FLASH.DIAG/BPM/2ORS/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3a405e70>, 'FLASH.DIAG/BPM/4ORS/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3a405ea0>, 'FLASH.DIAG/BPM/7ORS/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3a4061d0>, 'FLASH.DIAG/BPM/9ORS/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302fc40>, 'FLASH.DIAG/BPM/12ORS/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302ed70>, 'FLASH.DIAG/BPM/1SFUND2/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302ce50>, 'FLASH.DIAG/BPM/1SFUND3/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302cdf0>, 'FLASH.DIAG/BPM/1SFUND4/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302dc30>, 'FLASH.DIAG/BPM/1SFELC/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302fdf0>, 'FLASH.DIAG/BPM/1SMATCH/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302f460>, 'FLASH.DIAG/BPM/6SMATCH/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302e1a0>, 'FLASH.DIAG/BPM/13SMATCH/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302da80>, 'FLASH.DIAG/BPM/14SMATCH/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302f7f0>, 'FLASH.DIAG/BPM/5UND1/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302cfa0>, 'FLASH.DIAG/BPM/5UND2/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302ef50>, 'FLASH.DIAG/BPM/5UND3/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302f2b0>, 'FLASH.DIAG/BPM/5UND4/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302e770>, 'FLASH.DIAG/BPM/5UND5/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302ccd0>, 'FLASH.DIAG/BPM/5UND6/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302cf10>, 'FLASH.DIAG/BPM/3EXP/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302cd00>, 'FLASH.DIAG/BPM/9EXP/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302d0c0>, 'FLASH.DIAG/BPM/9DUMP/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302dc90>, 'FLASH.DIAG/BPM/10DUMP/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302d5d0>, 'FLASH.DIAG/BPM/13DUMP/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302d510>, 'FLASH.DIAG/BPM/15ACC7/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302f490>, 'FLASH.DIAG/BPM/19ACC7/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302e680>, 'FLASH.DIAG/BPM/1TCOL/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302f7c0>, 'FLASH.DIAG/BPM/6TCOL/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302fe20>, 'FLASH.DIAG/BPM/4FL2EXTR/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302f070>, 'FLASH.DIAG/BPM/5FL2EXTR/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302d210>, 'FLASH.DIAG/BPM/8FL2EXTR/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302d3c0>, 'FLASH.DIAG/BPM/11FL2EXTR/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302d990>, 'FLASH.DIAG/BPM/13FL2EXTR/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302f9d0>, 'FLASH.DIAG/BPM/15FL2EXTR/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302e410>, 'FLASH.DIAG/BPM/18FL2EXTR/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302d5a0>, 'FLASH.DIAG/BPM/21FL2EXTR/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302cee0>, 'FLASH.DIAG/BPM/22FL2EXTR/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302f430>, 'FLASH.DIAG/BPM/1FL2BC/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302f310>, 'FLASH.DIAG/BPM/6FL2BC/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302d360>, 'FLASH.DIAG/BPM/3FL2SEED3/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302e3e0>, 'FLASH.DIAG/BPM/3FL2SEED4/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302d870>, 'FLASH.DIAG/BPM/3FL2SEED5/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302d840>, 'FLASH.DIAG/BPM/3FL2SEED6/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302d180>, 'FLASH.DIAG/BPM/3FL2SASE2/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302d0f0>, 'FLASH.DIAG/BPM/3FL2SASE3/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302d330>, 'FLASH.DIAG/BPM/3FL2SASE4/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302eb00>, 'FLASH.DIAG/BPM/3FL2SASE5/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f32f23c40>, 'FLASH.DIAG/BPM/3FL2SASE7/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f32f231f0>, 'FLASH.DIAG/BPM/3FL2SASE8/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f32f23040>, 'FLASH.DIAG/BPM/3FL2SASE9/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302de70>, 'FLASH.DIAG/BPM/3FL2SASE10/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f32f200d0>, 'FLASH.DIAG/BPM/3FL2SASE11/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302f1c0>, 'FLASH.DIAG/BPM/3FL2SASE12/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302f4f0>, 'FLASH.DIAG/BPM/3FL2SASE13/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302f100>, 'FLASH.DIAG/BPM/3FL2BURN/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302dea0>, 'FLASH.DIAG/BPM/3FL2XTDS/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f3302eaa0>, 'FLASH.DIAG/BPM/9FL2XTDS/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f32f20580>, 'FLASH.DIAG/BPM/5FL2DUMP/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f32f20be0>, 'FLASH.DIAG/BPM/6FL2DUMP/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f32f20490>, 'FLASH.DIAG/BPM/10FL2DUMP/X.ALL': <doocspie.doocspie.readout.Readout object at 0x7f3f32f22740>}

All the returned readouts correspond to the same unique event as is shown in the following:

unique_events = {readout.event for readout in readouts.values()}  # using set comprehension

print("number of unique events:", len(unique_events))
number of unique events: 1

A Synchronizer instance always returns readouts once there is a new event, just like the TrainAbo. For the current number of properties, five subsequent event numbers are printed here:

for _ in range(5):
    readouts = synchronizer.get()
    print("event of property:",
          list(readouts.values())[0].event)  # all properties have the same event
event of property: 1966138771
event of property: 1966138772
event of property: 1966138773
event of property: 1966138774
event of property: 1966138775

If there are no usable properties for synchronization, a particular DoocspieException will be raised:

synchronizer = Synchronizer()  # trivially no usable properties for synchronization

try:
    synchronizer.get()
except doocspie.DoocspieException as exc:
    print("exception message:", exc.message)
exception message: no usable properties found

Duplicated property addresses and/or labels get caught before any readout can be returned:

synchronizer.add("TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/FLOAT.BUFFER")
synchronizer.add("TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/INTEGER.BUFFER", label="integer")

try:
    synchronizer.add("TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/FLOAT.BUFFER")
except doocspie.DoocspieException as exc:
    print("exception message:", exc.message)

try:
    synchronizer.add("TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/INTEGER", label="integer")
    synchronizer.update()  # duplicated labels get checked after updating
except doocspie.DoocspieException as exc:
    print("exception message:", exc.message)
exception message: address 'TEST.DOOCS/UNIT_TEST_SUPPORT/PY_DOOCS/FLOAT.BUFFER' already exists
exception message: label 'integer' already exists

The Synchronizer can be configured exactly the same as the TrainAbo, i.e. via list/tuples, dict or by means of the add method. It also accepts the label, offset, start, elements, timestamp_event and meta_event options, and we refer to the TrainAbo section for examples.