summaryrefslogtreecommitdiff
path: root/grc/core
diff options
context:
space:
mode:
authorSebastian Koslowski <koslowski@kit.edu>2017-01-25 20:57:02 +0100
committerSebastian Koslowski <koslowski@kit.edu>2017-01-25 20:57:02 +0100
commita4964a05c6f341ca23fd527712965ff8d173c1a3 (patch)
tree17c5e9df67597d51bd378894d51627cf85a3f9fa /grc/core
parent7c7fc1666e6ea393e537e084d5b393c43130b24d (diff)
parent3c78606949f6f40827244c4e338663f69344a171 (diff)
Merge branch 'master' into next
Diffstat (limited to 'grc/core')
-rw-r--r--grc/core/Port.py193
-rw-r--r--grc/core/generator/Generator.py6
2 files changed, 107 insertions, 92 deletions
diff --git a/grc/core/Port.py b/grc/core/Port.py
index 88601dc87a..138643503b 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
@@ -17,85 +17,116 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
"""
+from itertools import chain
+
from .Constants import DEFAULT_DOMAIN, GR_STREAM_DOMAIN, GR_MESSAGE_DOMAIN
from .Element import Element
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].get_source())
- 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.get_source(), _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.get_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(
- filter( # Get all virtual sinks with a matching stream id
- lambda vs: vs.get_param('stream_id').get_value() == vsp.get_parent().get_param('stream_id').get_value(),
- filter( # Get all enabled blocks that are also virtual sinks
- lambda b: b.is_virtual_sink(),
- vsp.get_parent().get_parent().get_enabled_blocks(),
- ),
- )[0].get_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.get_parent()
+ flow_graph = block.get_parent()
+
+ if not block.is_virtual_source():
+ return [source_port] # nothing to resolve, we're done
+
+ stream_id = block.get_param('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.get_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.get_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].get_sink())
- 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.get_sink(), _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.get_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.get_parent().get_param('stream_id').get_value(),
- filter( # Get all enabled blocks that are also virtual sinks
- lambda b: b.is_virtual_source(),
- vsp.get_parent().get_parent().get_enabled_blocks(),
- ),
- )[0].get_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.get_parent()
+ flow_graph = block.get_parent()
+
+ 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.get_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.get_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):
@@ -148,10 +179,9 @@ class Port(Element):
return Constants.TYPE_TO_SIZEOF.keys()
def is_type_empty(self):
- return not self._n['type']
+ return not self._n['type'] or not self.get_parent().resolve_dependencies(self._n['type'])
def validate(self):
- Element.validate(self)
if self.get_type() not in self.get_types():
self.add_error_message('Type "{}" is not a possible type.'.format(self.get_type()))
platform = self.get_parent().get_parent().get_parent()
@@ -164,18 +194,10 @@ class Port(Element):
"""
Handle the port cloning for virtual blocks.
"""
+ del self._error_messages[:]
if self.is_type_empty():
- 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)
+ self.resolve_empty_type()
+
hide = self.get_parent().resolve_dependencies(self._hide).strip().lower()
self._hide_evaluated = False if hide in ('false', 'off', '0') else bool(hide)
@@ -189,32 +211,25 @@ class Port(Element):
self._key = '0' # Is rectified in rewrite()
def resolve_virtual_source(self):
- if self.get_parent().is_virtual_sink():
- return _get_source_from_virtual_sink_port(self)
- if self.get_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:
- try:
- src = _get_source_from_virtual_sink_port(self)
- if not src.is_type_empty():
- return src
- except:
- pass
- sink = _get_sink_from_virtual_sink_port(self)
- if not sink.is_type_empty():
- return sink
- if self.is_source:
+ def find_port(finder):
try:
- src = _get_source_from_virtual_source_port(self)
- if not src.is_type_empty():
- return src
- except:
+ return next((p for p in finder(self) if not p.is_type_empty()), None)
+ except LoopError as error:
+ self.add_error_message(str(error))
+ except (StopIteration, Exception) as error:
pass
- sink = _get_sink_from_virtual_source_port(self)
- if not sink.is_type_empty():
- 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):
"""
diff --git a/grc/core/generator/Generator.py b/grc/core/generator/Generator.py
index 66b6f3fcff..bff70df8f5 100644
--- a/grc/core/generator/Generator.py
+++ b/grc/core/generator/Generator.py
@@ -167,10 +167,10 @@ class TopBlockGenerator(object):
# Get the virtual blocks and resolve their connections
virtual = filter(lambda c: c.get_source().get_parent().is_virtual_source(), connections)
for connection in virtual:
- source = connection.get_source().resolve_virtual_source()
sink = connection.get_sink()
- resolved = fg.get_parent().Connection(flow_graph=fg, porta=source, portb=sink)
- connections.append(resolved)
+ for source in connection.get_source().resolve_virtual_source():
+ resolved = fg.get_parent().Connection(flow_graph=fg, porta=source, portb=sink)
+ connections.append(resolved)
# Remove the virtual connection
connections.remove(connection)