Skip to content
This repository has been archived by the owner on Nov 3, 2021. It is now read-only.

Commit

Permalink
Merge pull request #1615 from catunlock/dependent_elements
Browse files Browse the repository at this point in the history
Udef checks dependencies
  • Loading branch information
reszelaz authored Jul 22, 2021
2 parents f0ac950 + 563e299 commit 33e06c9
Show file tree
Hide file tree
Showing 16 changed files with 143 additions and 53 deletions.
5 changes: 5 additions & 0 deletions src/sardana/macroserver/macros/test/test_scanct.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
##############################################################################

"""Tests for continuous scans (ct-like)"""
import json
import time
import PyTango
import unittest
Expand Down Expand Up @@ -343,6 +344,8 @@ def macro_stops(self, meas_config, macro_params, wait_timeout=None,
ScanctTest.check_stopped(self)

def tearDown(self):
for mg in self.pool.MotorGroupList:
self.pool.DeleteElement(json.loads(mg)['name'])
ScanctTest.tearDown(self)
unittest.TestCase.tearDown(self)

Expand Down Expand Up @@ -398,5 +401,7 @@ def macro_stops(self, meas_config, macro_params, wait_timeout=None,
ScanctTest.check_stopped(self)

def tearDown(self):
for mg in self.pool.MotorGroupList:
self.pool.DeleteElement(json.loads(mg)['name'])
ScanctTest.tearDown(self)
unittest.TestCase.tearDown(self)
22 changes: 20 additions & 2 deletions src/sardana/pool/pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

__docformat__ = 'restructuredtext'

import gc
import os.path
import logging.handlers

Expand Down Expand Up @@ -559,8 +560,25 @@ def delete_element(self, name):
elem = self.get_element(full_name=name)
except:
raise Exception("There is no element with name '%s'" % name)

elem_type = elem.get_type()

# cycle-reference may exist between the element and a traceback
# stored in SardanaValue or SardanaAttribute as a consequence of
# getting sys.exc_info() - try to delete them with gc.collect()
if elem.has_dependent_elements():
gc.collect()

dependent_elements = elem.get_dependent_elements()
if len(dependent_elements) > 0:
names = [elem.name for elem in dependent_elements]
raise Exception(
"The element {} can't be deleted because {} depend on it."
"\n\nIf the name of the dependent element starts with "
"'_mg_ms_*' it means that are motor groups, execute "
"DeleteElement(<motor_group_name>) command on the Pool e.g. "
"Pool_demo1_1.DeleteElement('_mg_ms_20671_1') in Spock."
.format(name, ", ".join(names)))

elem_type = elem.get_type()
if elem_type == ElementType.Controller:
if len(elem.get_elements()) > 0:
raise Exception("Cannot delete controller with elements. "
Expand Down
2 changes: 1 addition & 1 deletion src/sardana/pool/poolacquisition.py
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@ def run(self, *args, **kwargs):
# clean also the pseudo counters, even the ones that do not
# participate directly in the acquisition
for pseudo_elem in elem.get_pseudo_elements():
pseudo_elem.clear_value_buffer()
pseudo_elem().clear_value_buffer()

if self._hw_acq_args is not None:
self._hw_acq._wait()
Expand Down
26 changes: 18 additions & 8 deletions src/sardana/pool/poolbasechannel.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@

__docformat__ = 'restructuredtext'

import weakref

from sardana.sardanadefs import AttrQuality, ElementType
from sardana.sardanaattribute import SardanaAttribute
from sardana.sardanabuffer import SardanaBuffer
Expand All @@ -41,6 +43,7 @@
from sardana.sardanaevent import EventType
from sardana.pool import AcqSynch, AcqMode


class ValueBuffer(SardanaBuffer):

def is_value_required(self, idx):
Expand All @@ -53,7 +56,7 @@ def is_value_required(self, idx):
:rtype: bool
"""
for element in self.obj.get_pseudo_elements():
if element.get_value_buffer().next_idx <= idx:
if element().get_value_buffer().next_idx <= idx:
return True
return False

Expand Down Expand Up @@ -135,8 +138,8 @@ def get_pseudo_elements(self):
"""Returns list of pseudo elements e.g. pseudo counters that this
channel belongs to.
:return: pseudo elements
:rtype: seq<:class:`~sardana.pool.poolpseudocounter.PoolPseudoCounter`>
:return: weak references to pseudo elements
:rtype: seq<:class:`weakref.ref`>
"""
return self._pseudo_elements

Expand All @@ -150,7 +153,7 @@ def add_pseudo_element(self, element):
"""
if not self.has_pseudo_elements():
self.get_value_buffer().persistent = True
self._pseudo_elements.append(element)
self._pseudo_elements.append(weakref.ref(element))

def remove_pseudo_element(self, element):
"""Removes pseudo element e.g. pseudo counters that this channel
Expand All @@ -160,10 +163,17 @@ def remove_pseudo_element(self, element):
:type element:
:class:`~sardana.pool.poolpseudocounter.PoolPseudoCounter`
"""

self._pseudo_elements.remove(element)
if not self.has_pseudo_elements():
self.get_value_buffer().persistent = False
for pseudo_element in self._pseudo_elements:
if pseudo_element() == element:
self._pseudo_elements.remove(pseudo_element)
if not self.has_pseudo_elements():
self.get_value_buffer().persistent = False
break
else:
raise ValueError(
"{} is not a pseudo element of {}".format(
element.name, self.name)
)

def get_value_attribute(self):
"""Returns the value attribute object for this experiment channel
Expand Down
6 changes: 6 additions & 0 deletions src/sardana/pool/poolbaseelement.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ def serialize(self, *args, **kwargs):
ret = PoolObject.serialize(self, *args, **kwargs)
return ret

def get_dependent_elements(self):
return []

def has_dependent_elements(self):
return False

# --------------------------------------------------------------------------
# simulation mode
# --------------------------------------------------------------------------
Expand Down
4 changes: 2 additions & 2 deletions src/sardana/pool/poolcontroller.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,10 +442,10 @@ def set_operator(self, operator):
:param operator: the new operator object
:type operator: object"""
self._operator = operator
self._operator = weakref.ref(operator)

def get_operator(self):
return self._operator
return self._operator()

operator = property(fget=get_operator, fset=set_operator,
doc="current controller operator")
Expand Down
23 changes: 23 additions & 0 deletions src/sardana/pool/poolelement.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,29 @@ def set_action_cache(self, action_cache):
def get_source(self):
return "{0}/{1}".format(self.full_name, self.get_default_acquisition_channel())

def get_dependent_elements(self):
"""Get elements which depend on this element.
Get elements e.g. pseudo elements or groups, which depend on this
element.
:return: dependent elements
:rtype: seq<sardana.pool.poolbaseelement.PoolBaseElement>
"""
dependent_elements = []
for listener in self.get_listeners():
try:
elem = listener().__self__
except AttributeError:
continue
if isinstance(elem, PoolBaseElement):
dependent_elements.append(elem)

return dependent_elements

def has_dependent_elements(self):
return len(self.get_dependent_elements()) > 0

# --------------------------------------------------------------------------
# instrument
# --------------------------------------------------------------------------
Expand Down
18 changes: 12 additions & 6 deletions src/sardana/pool/poolmeasurementgroup.py
Original file line number Diff line number Diff line change
Expand Up @@ -800,7 +800,12 @@ def set_configuration_from_user(self, cfg):
params['pool'] = pool
channel = PoolExternalObject(**params)
else:
channel = pool.get_element_by_full_name(ch_name)
try:
channel = pool.get_element_by_full_name(ch_name)
except KeyError:
raise ValueError(
'{} is not defined'.format(ch_data['name']))

ch_data = self._fill_channel_data(channel, ch_data)
user_config_channel[ch_name] = ch_data
ch_item = ChannelConfiguration(channel, ch_data)
Expand Down Expand Up @@ -958,7 +963,11 @@ def set_configuration_from_user(self, cfg):
for conf_synch in conf_synch_ctrl.get_channels(enabled=True):
user_elem_ids_list.append(conf_synch.id)
self._parent.set_user_element_ids(user_elem_ids_list)

# force assignment of user elements to the measurement group
# in order to update the list of dependent elements (listeners)
# of the element e.g. to prevent undefinition of an element added
# to the measurement group.
self._parent.get_user_elements()
self.changed = True

def _fill_channel_data(self, channel, channel_data):
Expand Down Expand Up @@ -1352,16 +1361,13 @@ def prepare(self):
value = self._get_value()
self._pending_starts = self.nb_starts

kwargs = {'head': self}

self.acquisition.prepare(self.configuration,
self.acquisition_mode,
value,
self._synch_description,
self._moveable_obj,
self.sw_synch_initial_domain,
self.nb_starts,
**kwargs)
self.nb_starts)

def start_acquisition(self, value=None):
"""Start measurement.
Expand Down
3 changes: 3 additions & 0 deletions src/sardana/sardanaevent.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ def has_listeners(self):
return False
return len(self._listeners) > 0

def get_listeners(self):
return self._listeners

def fire_event(self, event_type, event_value, listeners=None):
self.flush_queue()
self._fire_event(event_type, event_value, listeners=listeners)
Expand Down
1 change: 1 addition & 0 deletions src/sardana/tango/pool/Controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ def set_ctrl(self, ctrl):
@DebugIt()
def delete_device(self):
PoolDevice.delete_device(self)
self.ctrl = None

@DebugIt()
def init_device(self):
Expand Down
1 change: 1 addition & 0 deletions src/sardana/tango/pool/MeasurementGroup.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ def delete_device(self):
mg = self.measurement_group
if mg is not None:
mg.remove_listener(self.on_measurement_group_changed)
self.measurement_group = None

@DebugIt()
def init_device(self):
Expand Down
1 change: 1 addition & 0 deletions src/sardana/tango/pool/MotorGroup.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ def delete_device(self):
motor_group = self.motor_group
if motor_group is not None:
motor_group.remove_listener(self.on_motor_group_changed)
self.motor_group = None

@DebugIt()
def init_device(self):
Expand Down
1 change: 1 addition & 0 deletions src/sardana/tango/pool/PseudoCounter.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ def delete_device(self):
pseudo_counter = self.pseudo_counter
if pseudo_counter is not None:
pseudo_counter.remove_listener(self.on_pseudo_counter_changed)
self.pseudo_counter = None

@DebugIt()
def init_device(self):
Expand Down
1 change: 1 addition & 0 deletions src/sardana/tango/pool/PseudoMotor.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ def delete_device(self):
pseudo_motor = self.pseudo_motor
if pseudo_motor is not None:
pseudo_motor.remove_listener(self.on_pseudo_motor_changed)
self.pseudo_motor = None

@DebugIt()
def init_device(self):
Expand Down
Loading

0 comments on commit 33e06c9

Please sign in to comment.