diff options
Diffstat (limited to 'grc/core/Port.py')
-rw-r--r-- | grc/core/Port.py | 206 |
1 files changed, 111 insertions, 95 deletions
diff --git a/grc/core/Port.py b/grc/core/Port.py index 41388df409..14dd9cd09f 100644 --- a/grc/core/Port.py +++ b/grc/core/Port.py @@ -1,5 +1,5 @@ """ -Copyright 2008-2015 Free Software Foundation, Inc. +Copyright 2008-2017 Free Software Foundation, Inc. This file is part of GNU Radio GNU Radio Companion is free software; you can redistribute it and/or @@ -19,86 +19,116 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA from __future__ import absolute_import +from itertools import chain + from six.moves import filter from .Element import Element, lazy_property - from . import Constants -def _get_source_from_virtual_sink_port(vsp): +class LoopError(Exception): + pass + + +def _upstream_ports(port): + if port.is_sink: + return _sources_from_virtual_sink_port(port) + else: + return _sources_from_virtual_source_port(port) + + +def _sources_from_virtual_sink_port(sink_port, _traversed=None): """ Resolve the source port that is connected to the given virtual sink port. Use the get source from virtual source to recursively resolve subsequent ports. """ - try: - return _get_source_from_virtual_source_port( - vsp.get_enabled_connections()[0].source_port) - except: - raise Exception('Could not resolve source for virtual sink port {}'.format(vsp)) + source_ports_per_virtual_connection = ( + # there can be multiple ports per virtual connection + _sources_from_virtual_source_port(c.source_port, _traversed) # type: list + for c in sink_port.get_enabled_connections() + ) + return list(chain(*source_ports_per_virtual_connection)) # concatenate generated lists of ports -def _get_source_from_virtual_source_port(vsp, traversed=[]): +def _sources_from_virtual_source_port(source_port, _traversed=None): """ Recursively resolve source ports over the virtual connections. Keep track of traversed sources to avoid recursive loops. """ - if not vsp.parent.is_virtual_source(): - return vsp - if vsp in traversed: - raise Exception('Loop found when resolving virtual source {}'.format(vsp)) - try: - return _get_source_from_virtual_source_port( - _get_source_from_virtual_sink_port( - list(filter( # Get all virtual sinks with a matching stream id - lambda vs: vs.get_param('stream_id').get_value() == vsp.parent.get_param('stream_id').get_value(), - list(filter( # Get all enabled blocks that are also virtual sinks - lambda b: b.is_virtual_sink(), - vsp.parent.parent.get_enabled_blocks(), - )), - ))[0].sinks[0] - ), traversed + [vsp], - ) - except: - raise Exception('Could not resolve source for virtual source port {}'.format(vsp)) - - -def _get_sink_from_virtual_source_port(vsp): + _traversed = set(_traversed or []) # a new set! + if source_port in _traversed: + raise LoopError('Loop found when resolving port type') + _traversed.add(source_port) + + block = source_port.parent_block + flow_graph = source_port.parent_flow_graph + + if not block.is_virtual_source(): + return [source_port] # nothing to resolve, we're done + + stream_id = block.get_parqam('stream_id').get_value() + + # currently the validation does not allow multiple virtual sinks and one virtual source + # but in the future it may... + connected_virtual_sink_blocks = ( + b for b in flow_graph.iter_enabled_blocks() + if b.is_virtual_sink() and b.get_param('stream_id').get_value() == stream_id + ) + source_ports_per_virtual_connection = ( + _sources_from_virtual_sink_port(b.sinks[0], _traversed) # type: list + for b in connected_virtual_sink_blocks + ) + return list(chain(*source_ports_per_virtual_connection)) # concatenate generated lists of ports + + +def _downstream_ports(port): + if port.is_source: + return _sinks_from_virtual_source_port(port) + else: + return _sinks_from_virtual_sink_port(port) + + +def _sinks_from_virtual_source_port(source_port, _traversed=None): """ Resolve the sink port that is connected to the given virtual source port. Use the get sink from virtual sink to recursively resolve subsequent ports. """ - try: - # Could have many connections, but use first - return _get_sink_from_virtual_sink_port( - vsp.get_enabled_connections()[0].sink_port) - except: - raise Exception('Could not resolve source for virtual source port {}'.format(vsp)) + sink_ports_per_virtual_connection = ( + # there can be multiple ports per virtual connection + _sinks_from_virtual_sink_port(c.sink_port, _traversed) # type: list + for c in source_port.get_enabled_connections() + ) + return list(chain(*sink_ports_per_virtual_connection)) # concatenate generated lists of ports -def _get_sink_from_virtual_sink_port(vsp, traversed=[]): +def _sinks_from_virtual_sink_port(sink_port, _traversed=None): """ Recursively resolve sink ports over the virtual connections. Keep track of traversed sinks to avoid recursive loops. """ - if not vsp.parent.is_virtual_sink(): - return vsp - if vsp in traversed: - raise Exception('Loop found when resolving virtual sink {}'.format(vsp)) - try: - return _get_sink_from_virtual_sink_port( - _get_sink_from_virtual_source_port( - filter( # Get all virtual source with a matching stream id - lambda vs: vs.get_param('stream_id').get_value() == vsp.parent.get_param('stream_id').get_value(), - list(filter( # Get all enabled blocks that are also virtual sinks - lambda b: b.is_virtual_source(), - vsp.parent.parent.get_enabled_blocks(), - )), - )[0].sources[0] - ), traversed + [vsp], - ) - except: - raise Exception('Could not resolve source for virtual sink port {}'.format(vsp)) + _traversed = set(_traversed or []) # a new set! + if sink_port in _traversed: + raise LoopError('Loop found when resolving port type') + _traversed.add(sink_port) + + block = sink_port.parent_block + flow_graph = sink_port.parent_flow_graph + + if not block.is_virtual_sink(): + return [sink_port] + + stream_id = block.get_param('stream_id').get_value() + + connected_virtual_source_blocks = ( + b for b in flow_graph.iter_enabled_blocks() + if b.is_virtual_source() and b.get_param('stream_id').get_value() == stream_id + ) + sink_ports_per_virtual_connection = ( + _sinks_from_virtual_source_port(b.sources[0], _traversed) # type: list + for b in connected_virtual_source_blocks + ) + return list(chain(*sink_ports_per_virtual_connection)) # concatenate generated lists of ports class Port(Element): @@ -140,6 +170,7 @@ class Port(Element): self._nports = n.get('nports', '') self._vlen = n.get('vlen', '') self._optional = bool(n.get('optional')) + self._optional_evaluated = False # Updated on rewrite() self.clones = [] # References to cloned ports (for nports > 1) def __str__(self): @@ -152,8 +183,7 @@ class Port(Element): Element.validate(self) if self.get_type() not in Constants.TYPE_TO_SIZEOF.keys(): self.add_error_message('Type "{}" is not a possible type.'.format(self.get_type())) - platform = self.parent_platform - if self.domain not in platform.domains: + if self.domain not in self.parent_platform.domains: self.add_error_message('Domain key "{}" is not registered.'.format(self.domain)) if not self.get_enabled_connections() and not self.get_optional(): self.add_error_message('Port is not connected.') @@ -162,20 +192,14 @@ class Port(Element): """ Handle the port cloning for virtual blocks. """ + del self._error_messages[:] if self.inherit_type: - try: - # Clone type and vlen - source = self.resolve_empty_type() - self._type = str(source.get_type()) - self._vlen = str(source.get_vlen()) - except: - # Reset type and vlen - self._type = '' - self._vlen = '' - - Element.rewrite(self) - hide = self.parent.resolve_dependencies(self._hide).strip().lower() + self.resolve_empty_type() + + hide = self.parent_block.resolve_dependencies(self._hide).strip().lower() self._hide_evaluated = False if hide in ('false', 'off', '0') else bool(hide) + optional = self.parent_block.resolve_dependencies(self._optional).strip().lower() + self._optional_evaluated = False if optional in ('false', 'off', '0') else bool(optional) # Update domain if was deduced from (dynamic) port type type_ = self.get_type() @@ -187,32 +211,25 @@ class Port(Element): self.key = '0' # Is rectified in rewrite() def resolve_virtual_source(self): - if self.parent.is_virtual_sink(): - return _get_source_from_virtual_sink_port(self) - if self.parent.is_virtual_source(): - return _get_source_from_virtual_source_port(self) + """Only used by Generator after validation is passed""" + return _upstream_ports(self) def resolve_empty_type(self): - if self.is_sink: + def find_port(finder): try: - src = _get_source_from_virtual_sink_port(self) - if not src.inherit_type: - return src - except: + return next((p for p in finder(self) if not p.inherit_type), None) + except LoopError as error: + self.add_error_message(str(error)) + except (StopIteration, Exception) as error: pass - sink = _get_sink_from_virtual_sink_port(self) - if not sink.inherit_type: - return sink - if self.is_source: - try: - src = _get_source_from_virtual_source_port(self) - if not src.inherit_type: - return src - except: - pass - sink = _get_sink_from_virtual_source_port(self) - if not sink.inherit_type: - return sink + + try: + port = find_port(_upstream_ports) or find_port(_downstream_ports) + self._type = str(port.get_type()) + self._vlen = str(port.get_vlen()) + except Exception: + # Reset type and vlen + self._type = self._vlen = '' def get_vlen(self): """ @@ -222,7 +239,7 @@ class Port(Element): Returns: the vector length or 1 """ - vlen = self.parent.resolve_dependencies(self._vlen) + vlen = self.parent_block.resolve_dependencies(self._vlen) try: return max(1, int(self.parent_flowgraph.evaluate(vlen))) except: @@ -240,14 +257,14 @@ class Port(Element): if self._nports == '': return 1 - nports = self.parent.resolve_dependencies(self._nports) + nports = self.parent_block.resolve_dependencies(self._nports) try: return max(1, int(self.parent_flowgraph.evaluate(nports))) except: return 1 def get_optional(self): - return bool(self._optional) + return self._optional_evaluated def add_clone(self): """ @@ -372,4 +389,3 @@ class PortClone(Port): def remove_clone(self, port): raise NotImplementedError() - |