summaryrefslogtreecommitdiff
path: root/grc/core/Port.py
diff options
context:
space:
mode:
Diffstat (limited to 'grc/core/Port.py')
-rw-r--r--grc/core/Port.py206
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()
-