""" Copyright 2008-2015 Free Software Foundation, Inc. This file is part of GNU Radio GNU Radio Companion is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GNU Radio Companion is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ from __future__ import absolute_import import collections from six.moves import range from . import Constants from .Element import Element, lazy_property class Connection(Element): is_connection = True def __init__(self, flow_graph, porta, portb): """ Make a new connection given the parent and 2 ports. Args: flow_graph: the parent of this element porta: a port (any direction) portb: a port (any direction) @throws Error cannot make connection Returns: a new connection """ Element.__init__(self, flow_graph) source, sink = self._get_sink_source(porta, portb) self.source_port = source self.sink_port = sink # Ensure that this connection (source -> sink) is unique for connection in flow_graph.connections: if connection.source_port is source and connection.sink_port is sink: raise LookupError('This connection between source and sink is not unique.') if self.is_bus(): self._make_bus_connect() else: self.parent_flowgraph.connect(source, sink) @staticmethod def _get_sink_source(porta, portb): source = sink = None # Separate the source and sink for port in (porta, portb): if port.is_source: source = port else: sink = port if not source: raise ValueError('Connection could not isolate source') if not sink: raise ValueError('Connection could not isolate sink') return source, sink def __str__(self): return 'Connection (\n\t{}\n\t\t{}\n\t{}\n\t\t{}\n)'.format( self.source_block, self.source_port, self.sink_block, self.sink_port, ) def is_msg(self): return self.source_port.get_type() == self.sink_port.get_type() == 'msg' def is_bus(self): return self.source_port.get_type() == 'bus' def validate(self): """ Validate the connections. The ports must match in io size. """ """ Validate the connections. The ports must match in type. """ Element.validate(self) platform = self.parent_platform source_domain = self.source_port.domain sink_domain = self.sink_port.domain if (source_domain, sink_domain) not in platform.connection_templates: self.add_error_message('No connection known for domains "{}", "{}"'.format( source_domain, sink_domain)) too_many_other_sinks = ( not platform.domains.get(source_domain, []).get('multiple_sinks', False) and len(self.source_port.get_enabled_connections()) > 1 ) too_many_other_sources = ( not platform.domains.get(sink_domain, []).get('multiple_sources', False) and len(self.sink_port.get_enabled_connections()) > 1 ) if too_many_other_sinks: self.add_error_message( 'Domain "{}" can have only one downstream block'.format(source_domain)) if too_many_other_sources: self.add_error_message( 'Domain "{}" can have only one upstream block'.format(sink_domain)) source_size = Constants.TYPE_TO_SIZEOF[self.source_port.get_type()] * self.source_port.get_vlen() sink_size = Constants.TYPE_TO_SIZEOF[self.sink_port.get_type()] * self.sink_port.get_vlen() if source_size != sink_size: self.add_error_message('Source IO size "{}" does not match sink IO size "{}".'.format(source_size, sink_size)) def get_enabled(self): """ Get the enabled state of this connection. Returns: true if source and sink blocks are enabled """ return self.source_block.get_enabled() and self.sink_block.get_enabled() @lazy_property def source_block(self): return self.source_port.parent_block @lazy_property def sink_block(self): return self.sink_port.parent_block ############################################## # Import/Export Methods ############################################## def export_data(self): """ Export this connection's info. Returns: a nested data odict """ n = collections.OrderedDict() n['source_block_id'] = self.source_block.get_id() n['sink_block_id'] = self.sink_block.get_id() n['source_key'] = self.source_port.key n['sink_key'] = self.sink_port.key return n def _make_bus_connect(self): source, sink = self.source_port, self.sink_port if source.get_type() == sink.get_type() == 'bus': raise ValueError('busses must get with busses') sources = source.get_associated_ports() sinks = sink.get_associated_ports() if len(sources) != len(sinks): raise ValueError('port connections must have same cardinality') for ports in zip(sources, sinks): self.parent_flowgraph.connect(*ports)