diff options
author | Sebastian Koslowski <koslowski@kit.edu> | 2016-02-07 13:44:59 +0100 |
---|---|---|
committer | Sebastian Koslowski <koslowski@kit.edu> | 2016-02-17 19:55:16 +0100 |
commit | ed6e9a484ddddbbadf19584f6606d2c9e36de823 (patch) | |
tree | bbad5da38afcfa2cfb29f4787dfe5d862565f5e4 /grc | |
parent | 62aadb3198ce82a6fb9d7e4a12bf7df1ee168100 (diff) |
grc-refactor: fixes, type-testing-flags, FlowGraph.py, (more)
Diffstat (limited to 'grc')
-rw-r--r-- | grc/gui/ActionHandler.py | 2 | ||||
-rw-r--r-- | grc/gui/Block.py | 19 | ||||
-rw-r--r-- | grc/gui/FlowGraph.py | 46 | ||||
-rw-r--r-- | grc/gui/Port.py | 18 | ||||
-rw-r--r-- | grc/model/Block.py | 11 | ||||
-rw-r--r-- | grc/model/Connection.py | 10 | ||||
-rw-r--r-- | grc/model/Constants.py | 8 | ||||
-rw-r--r-- | grc/model/Element.py | 33 | ||||
-rw-r--r-- | grc/model/FlowGraph.py | 763 | ||||
-rw-r--r-- | grc/model/Param.py | 5 | ||||
-rw-r--r-- | grc/model/Platform.py | 22 | ||||
-rw-r--r-- | grc/model/Port.py | 21 | ||||
-rw-r--r-- | grc/model/generator/Generator.py (renamed from grc/model/Generator.py) | 18 | ||||
-rw-r--r-- | grc/model/generator/__init__.py | 1 | ||||
-rw-r--r-- | grc/model/generator/flow_graph.tmpl (renamed from grc/model/flow_graph.tmpl) | 0 | ||||
-rw-r--r-- | grc/model/utils/__init__.py | 6 | ||||
-rw-r--r-- | grc/model/utils/complexity.py | 49 |
17 files changed, 451 insertions, 581 deletions
diff --git a/grc/gui/ActionHandler.py b/grc/gui/ActionHandler.py index 170247557a..b219ed2363 100644 --- a/grc/gui/ActionHandler.py +++ b/grc/gui/ActionHandler.py @@ -232,7 +232,7 @@ class ActionHandler: if y < y_min: y_min = y - for connection in block.get_connections(): + for connection in block.connections: # Get id of connected blocks source_id = connection.get_source().get_parent().get_id() diff --git a/grc/gui/Block.py b/grc/gui/Block.py index 97b4e96c91..2439c32d22 100644 --- a/grc/gui/Block.py +++ b/grc/gui/Block.py @@ -32,7 +32,7 @@ from .Constants import ( from . Element import Element from ..model.odict import odict from ..model.Param import num_to_str - +from ..model.utils.complexity import calculate_flowgraph_complexity from ..model.Block import Block as _Block BLOCK_MARKUP_TMPL="""\ @@ -138,10 +138,10 @@ class Block(Element, _Block): delta_coor: requested delta coordinate (dX, dY) to move Returns: - The delta coordinate possible to move while keeping the block on the canvas + The delta coordinate possible to move while keeping the block on the canvas or the input (dX, dY) on failure """ - dX, dY = delta_coor + dX, dY = delta_coor try: fgW, fgH = self.get_parent().get_size() @@ -150,7 +150,7 @@ class Block(Element, _Block): sW, sH = self.W, self.H else: sW, sH = self.H, self.W - + if x + dX < 0: dX = -x elif dX + x + sW >= fgW: @@ -162,7 +162,7 @@ class Block(Element, _Block): except: pass - return ( dX, dY ) + return ( dX, dY ) def get_rotation(self): """ @@ -196,7 +196,7 @@ class Block(Element, _Block): def create_labels(self): """Create the labels for the signal block.""" Element.create_labels(self) - self._bg_color = self.is_dummy_block() and Colors.MISSING_BLOCK_BACKGROUND_COLOR or \ + self._bg_color = self.is_dummy_block and Colors.MISSING_BLOCK_BACKGROUND_COLOR or \ self.get_bypassed() and Colors.BLOCK_BYPASSED_COLOR or \ self.get_enabled() and Colors.BLOCK_ENABLED_COLOR or Colors.BLOCK_DISABLED_COLOR @@ -207,7 +207,7 @@ class Block(Element, _Block): layout.set_markup(Utils.parse_template(BLOCK_MARKUP_TMPL, block=self, font=BLOCK_FONT)) self.label_width, self.label_height = layout.get_pixel_size() #display the params - if self.is_dummy_block(): + if self.is_dummy_block: markups = [ '<span foreground="black" font_desc="{font}"><b>key: </b>{key}</span>'.format(font=PARAM_FONT, key=self._key) ] @@ -274,7 +274,8 @@ class Block(Element, _Block): # Show the flowgraph complexity on the top block if enabled if Actions.TOGGLE_SHOW_FLOWGRAPH_COMPLEXITY.get_active() and self.get_key() == "options": - complexity = "Complexity: {}bal".format(num_to_str(self.get_parent().get_complexity())) + complexity = calculate_flowgraph_complexity(self.get_parent()) + complexity = "Complexity: {}bal".format(num_to_str(complexity)) layout = gtk.DrawingArea().create_pango_layout('') layout.set_markup(Utils.parse_template(COMMENT_COMPLEXITY_MARKUP_TMPL, @@ -314,7 +315,7 @@ class Block(Element, _Block): Element.draw( self, gc, window, bg_color=self._bg_color, border_color=self.is_highlighted() and Colors.HIGHLIGHT_COLOR or - self.is_dummy_block() and Colors.MISSING_BLOCK_BORDER_COLOR or Colors.BORDER_COLOR, + self.is_dummy_block and Colors.MISSING_BLOCK_BORDER_COLOR or Colors.BORDER_COLOR, ) #draw label image if self.is_horizontal(): diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py index e975595c11..0c66a7e5ad 100644 --- a/grc/gui/FlowGraph.py +++ b/grc/gui/FlowGraph.py @@ -19,7 +19,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA import random import functools -from itertools import chain +from itertools import chain, count from operator import methodcaller from distutils.spawn import find_executable @@ -65,6 +65,22 @@ class FlowGraph(Element, _Flowgraph): self._external_updaters = {} + def _get_unique_id(self, base_id=''): + """ + Get a unique id starting with the base id. + + Args: + base_id: the id starts with this and appends a count + + Returns: + a unique id + """ + for index in count(): + block_id = '{}_{}'.format(base_id, index) + if block_id not in (b.get_id() for b in self.blocks): + break + return block_id + def install_external_editor(self, param): target = (param.get_parent().get_id(), param.get_key()) @@ -163,7 +179,7 @@ class FlowGraph(Element, _Flowgraph): #get connections between selected blocks connections = filter( lambda c: c.get_source().get_parent() in blocks and c.get_sink().get_parent() in blocks, - self.get_connections(), + self.connections, ) clipboard = ( (x_min, y_min), @@ -202,7 +218,7 @@ class FlowGraph(Element, _Flowgraph): if param_key == 'id': old_id2block[param_value] = block #if the block id is not unique, get a new block id - if param_value in [bluck.get_id() for bluck in self.get_blocks()]: + if param_value in (blk.get_id() for blk in self.blocks): param_value = self._get_unique_id(param_value) #set value to key block.get_param(param_key).set_value(param_value) @@ -284,7 +300,7 @@ class FlowGraph(Element, _Flowgraph): """ for selected_block in self.get_selected_blocks(): delta_coordinate = selected_block.bound_move_delta(delta_coordinate) - + for selected_block in self.get_selected_blocks(): selected_block.move(delta_coordinate) self.element_moved = True @@ -346,7 +362,7 @@ class FlowGraph(Element, _Flowgraph): # draw comments first if Actions.TOGGLE_SHOW_BLOCK_COMMENTS.get_active(): - for block in self.iter_blocks(): + for block in self.blocks: if block.get_enabled(): block.draw_comment(gc, window) #draw multi select rectangle @@ -364,8 +380,8 @@ class FlowGraph(Element, _Flowgraph): window.draw_rectangle(gc, False, x, y, w, h) #draw blocks on top of connections hide_disabled_blocks = Actions.TOGGLE_HIDE_DISABLED_BLOCKS.get_active() - blocks = sorted(self.iter_blocks(), key=methodcaller('get_enabled')) - for element in chain(self.iter_connections(), blocks): + blocks = sorted(self.blocks, key=methodcaller('get_enabled')) + for element in chain(self.connections, blocks): if hide_disabled_blocks and not element.get_enabled(): continue # skip hidden disabled blocks and connections element.draw(gc, window) @@ -452,13 +468,13 @@ class FlowGraph(Element, _Flowgraph): if not selected_element: continue # hidden disabled connections, blocks and their ports can not be selected if Actions.TOGGLE_HIDE_DISABLED_BLOCKS.get_active() and ( - selected_element.is_block() and not selected_element.get_enabled() or - selected_element.is_connection() and not selected_element.get_enabled() or - selected_element.is_port() and not selected_element.get_parent().get_enabled() + selected_element.is_block and not selected_element.get_enabled() or + selected_element.is_connection and not selected_element.get_enabled() or + selected_element.is_port and not selected_element.get_parent().get_enabled() ): continue #update the selected port information - if selected_element.is_port(): + if selected_element.is_port: if not coor_m: selected_port = selected_element selected_element = selected_element.get_parent() selected.add(selected_element) @@ -482,7 +498,8 @@ class FlowGraph(Element, _Flowgraph): """ selected = set() for selected_element in self.get_selected_elements(): - if selected_element.is_connection(): selected.add(selected_element) + if selected_element.is_connection: + selected.add(selected_element) return list(selected) def get_selected_blocks(self): @@ -494,7 +511,8 @@ class FlowGraph(Element, _Flowgraph): """ selected = set() for selected_element in self.get_selected_elements(): - if selected_element.is_block(): selected.add(selected_element) + if selected_element.is_block: + selected.add(selected_element) return list(selected) def get_selected_block(self): @@ -669,7 +687,7 @@ class FlowGraph(Element, _Flowgraph): adj.set_value(adj_val-SCROLL_DISTANCE) adj.emit('changed') #remove the connection if selected in drag event - if len(self.get_selected_elements()) == 1 and self.get_selected_element().is_connection(): + if len(self.get_selected_elements()) == 1 and self.get_selected_element().is_connection: Actions.ELEMENT_DELETE() #move the selected elements and record the new coordinate if not self.get_ctrl_mask(): diff --git a/grc/gui/Port.py b/grc/gui/Port.py index 28d3b4d915..c281cbdeeb 100644 --- a/grc/gui/Port.py +++ b/grc/gui/Port.py @@ -64,7 +64,7 @@ class Port(_Port, Element): rotation = self.get_rotation() #get all sibling ports ports = self.get_parent().get_sources_gui() \ - if self.is_source() else self.get_parent().get_sinks_gui() + if self.is_source else self.get_parent().get_sinks_gui() ports = filter(lambda p: not p.get_hide(), ports) #get the max width self.W = max([port.W for port in ports] + [PORT_MIN_WIDTH]) @@ -82,27 +82,27 @@ class Port(_Port, Element): index = length-index-1 port_separation = PORT_SEPARATION \ - if not self.get_parent().has_busses[self.is_source()] \ + if not self.get_parent().has_busses[self.is_source] \ else max([port.H for port in ports]) + PORT_SPACING offset = (self.get_parent().H - (length-1)*port_separation - self.H)/2 #create areas and connector coordinates - if (self.is_sink() and rotation == 0) or (self.is_source() and rotation == 180): + if (self.is_sink and rotation == 0) or (self.is_source and rotation == 180): x = -W y = port_separation*index+offset self.add_area((x, y), (W, self.H)) self._connector_coordinate = (x-1, y+self.H/2) - elif (self.is_source() and rotation == 0) or (self.is_sink() and rotation == 180): + elif (self.is_source and rotation == 0) or (self.is_sink and rotation == 180): x = self.get_parent().W y = port_separation*index+offset self.add_area((x, y), (W, self.H)) self._connector_coordinate = (x+1+W, y+self.H/2) - elif (self.is_source() and rotation == 90) or (self.is_sink() and rotation == 270): + elif (self.is_source and rotation == 90) or (self.is_sink and rotation == 270): y = -W x = port_separation*index+offset self.add_area((x, y), (self.H, W)) self._connector_coordinate = (x+self.H/2, y-1) - elif (self.is_sink() and rotation == 90) or (self.is_source() and rotation == 270): + elif (self.is_sink and rotation == 90) or (self.is_source and rotation == 270): y = self.get_parent().W x = port_separation*index+offset self.add_area((x, y), (self.H, W)) @@ -145,7 +145,7 @@ class Port(_Port, Element): Element.draw( self, gc, window, bg_color=self._bg_color, border_color=self.is_highlighted() and Colors.HIGHLIGHT_COLOR or - self.get_parent().is_dummy_block() and Colors.MISSING_BLOCK_BORDER_COLOR or + self.get_parent().is_dummy_block and Colors.MISSING_BLOCK_BORDER_COLOR or Colors.BORDER_COLOR, ) if not self._areas_list or self._label_hidden(): @@ -177,8 +177,8 @@ class Port(_Port, Element): Returns: the direction in degrees """ - if self.is_source(): return self.get_rotation() - elif self.is_sink(): return (self.get_rotation() + 180)%360 + if self.is_source: return self.get_rotation() + elif self.is_sink: return (self.get_rotation() + 180)%360 def get_connector_length(self): """ diff --git a/grc/model/Block.py b/grc/model/Block.py index ae3169c44a..8af3e98456 100644 --- a/grc/model/Block.py +++ b/grc/model/Block.py @@ -71,6 +71,8 @@ def _get_elem(lst, key): class Block(Element): + is_block = True + def __init__(self, flow_graph, n): """ Make a new block from nested data. @@ -237,6 +239,11 @@ class Block(Element): self._epy_source_hash = -1 # for epy blocks self._epy_reload_error = None + if self._bussify_sink: + self.bussify({'name': 'bus', 'type': 'bus'}, 'sink') + if self._bussify_source: + self.bussify({'name': 'bus', 'type': 'bus'}, 'source') + def get_bus_structure(self, direction): if direction == 'source': bus_structure = self._bus_structure_source @@ -300,6 +307,7 @@ class Block(Element): """ Add and remove ports to adjust for the nports. """ + Element.rewrite(self) # Check and run any custom rewrite function for this block getattr(self, 'rewrite_' + self._key, lambda: None)() @@ -601,9 +609,6 @@ class Block(Element): def get_id(self): return self.get_param('id').get_value() - def is_block(self): - return True - def get_name(self): return self._name diff --git a/grc/model/Connection.py b/grc/model/Connection.py index 7cef4ad2b7..a7b428dfe6 100644 --- a/grc/model/Connection.py +++ b/grc/model/Connection.py @@ -25,6 +25,8 @@ from .odict import odict class Connection(Element): + is_connection = True + def __init__(self, flow_graph, porta, portb): """ Make a new connection given the parent and 2 ports. @@ -42,9 +44,9 @@ class Connection(Element): source = sink = None # Separate the source and sink for port in (porta, portb): - if port.is_source(): + if port.is_source: source = port - if port.is_sink(): + else: sink = port if not source: raise ValueError('Connection could not isolate source') @@ -57,7 +59,7 @@ class Connection(Element): if not len(source.get_associated_ports()) == len(sink.get_associated_ports()): raise ValueError('port connections must have same cardinality') # Ensure that this connection (source -> sink) is unique - for connection in self.get_parent().get_connections(): + for connection in flow_graph.connections: if connection.get_source() is source and connection.get_sink() is sink: raise LookupError('This connection between source and sink is not unique.') self._source = source @@ -81,8 +83,6 @@ class Connection(Element): self.get_sink(), ) - def is_connection(self): return True - def is_msg(self): return self.get_source().get_type() == self.get_sink().get_type() == 'msg' diff --git a/grc/model/Constants.py b/grc/model/Constants.py index d77dffcd5e..f1dae1d953 100644 --- a/grc/model/Constants.py +++ b/grc/model/Constants.py @@ -45,6 +45,8 @@ BLOCKS_DIRS = filter( # filter blank strings DATA_DIR = os.path.dirname(__file__) FLOW_GRAPH_DTD = os.path.join(DATA_DIR, 'flow_graph.dtd') BLOCK_TREE_DTD = os.path.join(DATA_DIR, 'block_tree.dtd') +BLOCK_DTD = os.path.join(DATA_DIR, 'block.dtd') +DEFAULT_FLOW_GRAPH = os.path.join(DATA_DIR, 'default_flow_graph.grc') # File format versions: # 0: undefined / legacy @@ -79,12 +81,6 @@ TOP_BLOCK_FILE_MODE = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP stat.S_IWGRP | stat.S_IXGRP | stat.S_IROTH HIER_BLOCK_FILE_MODE = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH -# Data files -DATA_DIR = os.path.dirname(__file__) -FLOW_GRAPH_TEMPLATE = os.path.join(DATA_DIR, 'flow_graph.tmpl') -BLOCK_DTD = os.path.join(DATA_DIR, 'block.dtd') -DEFAULT_FLOW_GRAPH = os.path.join(DATA_DIR, 'default_flow_graph.grc') - # Define types, native python + numpy VECTOR_TYPES = (tuple, list, set, numpy.ndarray) COMPLEX_TYPES = [complex, numpy.complex, numpy.complex64, numpy.complex128] diff --git a/grc/model/Element.py b/grc/model/Element.py index 6716cd619c..c999d6704f 100644 --- a/grc/model/Element.py +++ b/grc/model/Element.py @@ -90,34 +90,17 @@ class Element(object): return list() ############################################## - # Type testing methods + # Type testing ############################################## - def is_element(self): - return True - - def is_platform(self): - return False - - def is_flow_graph(self): - return False - - def is_connection(self): - return False - - def is_block(self): - return False + is_platform = False - def is_dummy_block(self): - return False + is_flow_graph = False - def is_source(self): - return False + is_block = False + is_dummy_block = False - def is_sink(self): - return False + is_connection = False - def is_port(self): - return False + is_port = False - def is_param(self): - return False + is_param = False diff --git a/grc/model/FlowGraph.py b/grc/model/FlowGraph.py index 0aea1457cb..fd391c6b32 100644 --- a/grc/model/FlowGraph.py +++ b/grc/model/FlowGraph.py @@ -1,26 +1,25 @@ -""" -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 -""" +# 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 + import re import imp import time from operator import methodcaller -from itertools import ifilter +from itertools import ifilter, chain from ..gui import Messages @@ -38,36 +37,10 @@ _bus_struct_sink_searcher = re.compile('^(bus_structure_sink)$') _bus_struct_src_searcher = re.compile('^(bus_structure_source)$') -def _initialize_dummy_block(block, block_n): - """ - This is so ugly... dummy-fy a block - Modify block object to get the behaviour for a missing block - """ - - block._key = block_n.find('key') - block.is_dummy_block = lambda: True - block.is_valid = lambda: False - block.get_enabled = lambda: False - for param_n in block_n.findall('param'): - if param_n['key'] not in block.get_param_keys(): - new_param_n = odict({'key': param_n['key'], 'name': param_n['key'], 'type': 'string'}) - params = block.get_parent().get_parent().Param(block=block, n=new_param_n) - block.get_params().append(params) - - -def _dummy_block_add_port(block, key, dir): - """ This is so ugly... Add a port to a dummy-field block """ - port_n = odict({'name': '?', 'key': key, 'type': ''}) - port = block.get_parent().get_parent().Port(block=block, n=port_n, dir=dir) - if port.is_source(): - block.get_sources().append(port) - else: - block.get_sinks().append(port) - return port - - class FlowGraph(Element): + is_flow_graph = True + def __init__(self, platform): """ Make a flow graph from the arguments. @@ -78,214 +51,27 @@ class FlowGraph(Element): Returns: the flow graph object """ - self.grc_file_path = '' - # Initialize Element.__init__(self, platform) self._elements = [] self._timestamp = time.ctime() - # Inital blank import - self.import_data() - self.n = {} - self.n_hash = -1 - self._renew_eval_ns = True - self._eval_cache = {} + self.platform = platform # todo: make this a lazy prop + self.blocks = [] + self.connections = [] - def _get_unique_id(self, base_id=''): - """ - Get a unique id starting with the base id. + self._eval_cache = {} + self.namespace = {} - Args: - base_id: the id starts with this and appends a count + self.grc_file_path = '' - Returns: - a unique id - """ - index = 0 - while True: - id = '{}_{}'.format(base_id, index) - index += 1 - # Make sure that the id is not used by another block - if not filter(lambda b: b.get_id() == id, self.get_blocks()): return id + self.import_data() def __str__(self): return 'FlowGraph - {}({})'.format(self.get_option('title'), self.get_option('id')) - def get_complexity(self): - """ Determines the complexity of a flowgraph """ - dbal = 0 - block_list = self.get_blocks() - for block in block_list: - # Skip options block - if block.get_key() == 'options': - continue - - # Don't worry about optional sinks? - sink_list = filter(lambda c: not c.get_optional(), block.get_sinks()) - source_list = filter(lambda c: not c.get_optional(), block.get_sources()) - sinks = float(len(sink_list)) - sources = float(len(source_list)) - base = max(min(sinks, sources), 1) - - # Port ratio multiplier - if min(sinks, sources) > 0: - multi = sinks / sources - multi = (1 / multi) if multi > 1 else multi - else: - multi = 1 - - # Connection ratio multiplier - sink_multi = max(float(sum(map(lambda c: len(c.get_connections()), sink_list)) / max(sinks, 1.0)), 1.0) - source_multi = max(float(sum(map(lambda c: len(c.get_connections()), source_list)) / max(sources, 1.0)), 1.0) - dbal = dbal + (base * multi * sink_multi * source_multi) - - elements = float(len(self.get_elements())) - connections = float(len(self.get_connections())) - disabled_connections = len(filter(lambda c: not c.get_enabled(), self.get_connections())) - blocks = float(len(block_list)) - variables = elements - blocks - connections - enabled = float(len(self.get_enabled_blocks())) - - # Disabled multiplier - if enabled > 0: - disabled_multi = 1 / (max(1 - ((blocks - enabled) / max(blocks, 1)), 0.05)) - else: - disabled_multi = 1 - - # Connection multiplier (How many connections ) - if (connections - disabled_connections) > 0: - conn_multi = 1 / (max(1 - (disabled_connections / max(connections, 1)), 0.05)) - else: - conn_multi = 1 - - final = round(max((dbal - 1) * disabled_multi * conn_multi * connections, 0.0) / 1000000, 6) - return final - - def _eval(self, code, namespace, namespace_hash): - """ - Evaluate the code with the given namespace. - - Args: - code: a string with python code - namespace: a dict representing the namespace - namespace_hash: a unique hash for the namespace - - Returns: - the resultant object - """ - if not code: - raise Exception('Cannot evaluate empty statement.') - my_hash = hash(code) ^ namespace_hash - - # Cache if does not exist - if my_hash not in self._eval_cache: - self._eval_cache[my_hash] = eval(code, namespace, namespace) - # Return from cache - return self._eval_cache[my_hash] - - def get_hier_block_stream_io(self, direction): - """ - Get a list of stream io signatures for this flow graph. - - Args: - direction: a string of 'in' or 'out' - - Returns: - a list of dicts with: type, label, vlen, size, optional - """ - return filter(lambda p: p['type'] != "message", - self.get_hier_block_io(direction)) - - def get_hier_block_message_io(self, direction): - """ - Get a list of message io signatures for this flow graph. - - Args: - direction: a string of 'in' or 'out' - - Returns: - a list of dicts with: type, label, vlen, size, optional - """ - return filter(lambda p: p['type'] == "message", - self.get_hier_block_io(direction)) - - def get_hier_block_io(self, direction): - """ - Get a list of io ports for this flow graph. - - Args: - direction: a string of 'in' or 'out' - - Returns: - a list of dicts with: type, label, vlen, size, optional - """ - pads = self.get_pad_sources() if direction in ('sink', 'in') else \ - self.get_pad_sinks() if direction in ('source', 'out') else [] - ports = [] - for pad in pads: - master = { - 'label': str(pad.get_param('label').get_evaluated()), - 'type': str(pad.get_param('type').get_evaluated()), - 'vlen': str(pad.get_param('vlen').get_value()), - 'size': pad.get_param('type').get_opt('size'), - 'optional': bool(pad.get_param('optional').get_evaluated()), - } - num_ports = pad.get_param('num_streams').get_evaluated() - if num_ports > 1: - for i in xrange(num_ports): - clone = master.copy() - clone['label'] += str(i) - ports.append(clone) - else: - ports.append(master) - return ports - - def get_pad_sources(self): - """ - Get a list of pad source blocks sorted by id order. - - Returns: - a list of pad source blocks in this flow graph - """ - pads = filter(lambda b: b.get_key() == 'pad_source', self.get_enabled_blocks()) - return sorted(pads, lambda x, y: cmp(x.get_id(), y.get_id())) - - def get_pad_sinks(self): - """ - Get a list of pad sink blocks sorted by id order. - - Returns: - a list of pad sink blocks in this flow graph - """ - pads = filter(lambda b: b.get_key() == 'pad_sink', self.get_enabled_blocks()) - return sorted(pads, lambda x, y: cmp(x.get_id(), y.get_id())) - - def get_pad_port_global_key(self, port): - """ - Get the key for a port of a pad source/sink to use in connect() - This takes into account that pad blocks may have multiple ports - - Returns: - the key (str) - """ - key_offset = 0 - pads = self.get_pad_sources() if port.is_source() else self.get_pad_sinks() - for pad in pads: - # using the block param 'type' instead of the port domain here - # to emphasize that hier block generation is domain agnostic - is_message_pad = pad.get_param('type').get_evaluated() == "message" - if port.get_parent() == pad: - if is_message_pad: - key = pad.get_param('label').get_value() - else: - key = str(key_offset + int(port.get_key())) - return key - else: - # assuming we have either only sources or sinks - if not is_message_pad: - key_offset += len(pad.get_ports()) - return -1 - + ############################################## + # TODO: Move these to new generator package + ############################################## def get_imports(self): """ Get a set of all import statments in this flow graph namespace. @@ -362,7 +148,7 @@ class FlowGraph(Element): """ Get an iterator of all blocks that are enabled and not bypassed. """ - return ifilter(methodcaller('get_enabled'), self.iter_blocks()) + return ifilter(methodcaller('get_enabled'), self.blocks) def get_enabled_blocks(self): """ @@ -380,7 +166,7 @@ class FlowGraph(Element): Returns: a list of blocks """ - return filter(methodcaller('get_bypassed'), self.iter_blocks()) + return filter(methodcaller('get_bypassed'), self.blocks) def get_enabled_connections(self): """ @@ -389,68 +175,7 @@ class FlowGraph(Element): Returns: a list of connections """ - return filter(methodcaller('get_enabled'), self.get_connections()) - - def _get_new_block(self, key): - """ - Get a new block of the specified key. - Add the block to the list of elements. - - Args: - key: the block key - - Returns: - the new block or None if not found - """ - if key not in self.get_parent().get_block_keys(): - return None - block = self.get_parent().get_new_block(self, key) - self.get_elements().append(block) - if block._bussify_sink: - block.bussify({'name': 'bus', 'type': 'bus'}, 'sink') - if block._bussify_source: - block.bussify({'name': 'bus', 'type': 'bus'}, 'source') - return block - - def connect(self, porta, portb): - """ - Create a connection between porta and portb. - - Args: - porta: a port - portb: another port - @throw Exception bad connection - - Returns: - the new connection - """ - connection = self.get_parent().Connection(flow_graph=self, porta=porta, portb=portb) - self.get_elements().append(connection) - return connection - - def remove_element(self, element): - """ - Remove the element from the list of elements. - If the element is a port, remove the whole block. - If the element is a block, remove its connections. - If the element is a connection, just remove the connection. - """ - if element not in self.get_elements(): - return - # Found a port, set to parent signal block - if element.is_port(): - element = element.get_parent() - # Remove block, remove all involved connections - if element.is_block(): - for port in element.get_ports(): - map(self.remove_element, port.get_connections()) - if element.is_connection(): - if element.is_bus(): - cons_list = [] - for i in map(lambda a: a.get_connections(), element.get_source().get_associated_ports()): - cons_list.extend(i) - map(self.remove_element, cons_list) - self.get_elements().remove(element) + return filter(methodcaller('get_enabled'), self.connections) def get_option(self, key): """ @@ -465,29 +190,14 @@ class FlowGraph(Element): """ return self._options_block.get_param(key).get_evaluated() - def is_flow_graph(self): - return True - ############################################## # Access Elements ############################################## def get_block(self, id): - for block in self.iter_blocks(): + for block in self.blocks: if block.get_id() == id: return block - raise KeyError('No block with ID {0!r}'.format(id)) - - def iter_blocks(self): - return ifilter(methodcaller('is_block'), self.get_elements()) - - def get_blocks(self): - return list(self.iter_blocks()) - - def iter_connections(self): - return ifilter(methodcaller('is_connection'), self.get_elements()) - - def get_connections(self): - return list(self.iter_connections()) + raise KeyError('No block with ID {!r}'.format(id)) def get_elements(self): """ @@ -497,75 +207,65 @@ class FlowGraph(Element): Returns: the element list """ - options_block_count = self._elements.count(self._options_block) + options_block_count = self.blocks.count(self._options_block) if not options_block_count: - self._elements.append(self._options_block) + self.blocks.append(self._options_block) for i in range(options_block_count-1): - self._elements.remove(self._options_block) - return self._elements + self.blocks.remove(self._options_block) + + return self.blocks + self.connections get_children = get_elements - # TODO >>> THIS SUCKS # def rewrite(self): """ Flag the namespace to be renewed. """ - def reconnect_bus_blocks(): - for block in self.get_blocks(): - - if 'bus' in map(lambda a: a.get_type(), block.get_sources_gui()): - for i in range(len(block.get_sources_gui())): - if len(block.get_sources_gui()[i].get_connections()) > 0: - source = block.get_sources_gui()[i] - sink = [] - for j in range(len(source.get_connections())): - sink.append(source.get_connections()[j].get_sink()) - for elt in source.get_connections(): - self.remove_element(elt) - for j in sink: - self.connect(source, j) - self._renew_eval_ns = True - - def refactor_bus_structure(): - - for block in self.get_blocks(): - for direc in ['source', 'sink']: - if direc == 'source': - get_p = block.get_sources - get_p_gui = block.get_sources_gui - bus_structure = block.form_bus_structure('source') - else: - get_p = block.get_sinks - get_p_gui = block.get_sinks_gui - bus_structure = block.form_bus_structure('sink') - - if 'bus' in map(lambda a: a.get_type(), get_p_gui()): - if len(get_p_gui()) > len(bus_structure): - times = range(len(bus_structure), len(get_p_gui())) - for i in times: - for connect in get_p_gui()[-1].get_connections(): - block.get_parent().remove_element(connect) - get_p().remove(get_p_gui()[-1]) - elif len(get_p_gui()) < len(bus_structure): - n = {'name': 'bus', 'type': 'bus'} - if True in map(lambda a: isinstance(a.get_nports(), int), get_p()): - n['nports'] = str(1) - - times = range(len(get_p_gui()), len(bus_structure)) - - for i in times: - n['key'] = str(len(get_p())) - n = odict(n) - port = block.get_parent().get_parent().Port(block=block, n=n, dir=direc) - get_p().append(port) - - for child in self.get_children(): + self.renew_namespace() + for child in chain(self.blocks, self.connections): child.rewrite() - refactor_bus_structure() - reconnect_bus_blocks() + self.bus_ports_rewrite() + + def renew_namespace(self): + namespace = {} + # Load imports + for expr in self.get_imports(): + try: + exec expr in namespace + except: + pass + + for id, expr in self.get_python_modules(): + try: + module = imp.new_module(id) + exec expr in module.__dict__ + namespace[id] = module + except: + pass + + # Load parameters + np = {} # params don't know each other + for parameter in self.get_parameters(): + try: + value = eval(parameter.get_param('value').to_code(), namespace) + np[parameter.get_id()] = value + except: + pass + namespace.update(np) # Merge param namespace + + # Load variables + for variable in self.get_variables(): + try: + value = eval(variable.get_var_value(), namespace) + namespace[variable.get_id()] = value + except: + pass + + self.namespace.clear() + self._eval_cache.clear() + self.namespace.update(namespace) def evaluate(self, expr): """ @@ -578,66 +278,76 @@ class FlowGraph(Element): Returns: the evaluated data """ - if self._renew_eval_ns: - self._renew_eval_ns = False - # Reload namespace - n = dict() - # Load imports - for code in self.get_imports(): - try: - exec code in n - except: - pass - - for id, code in self.get_python_modules(): - try: - module = imp.new_module(id) - exec code in module.__dict__ - n[id] = module - except: - pass - - # Load parameters - np = dict() - for parameter in self.get_parameters(): - try: - e = eval(parameter.get_param('value').to_code(), n, n) - np[parameter.get_id()] = e - except: - pass - n.update(np) # Merge param namespace - # Load variables - for variable in self.get_variables(): - try: - e = eval(variable.get_var_value(), n, n) - n[variable.get_id()] = e - except: - pass - # Make namespace public - self.n = n - self.n_hash = hash(str(n)) # Evaluate - e = self._eval(expr, self.n, self.n_hash) - return e + if not expr: + raise Exception('Cannot evaluate empty statement.') + return self._eval_cache.setdefault(expr, eval(expr, self.namespace)) + + ############################################## + # Add/remove stuff + ############################################## def get_new_block(self, key): - """Try to auto-generate the block from file if missing""" - block = self._get_new_block(key) - if not block: - platform = self.get_parent() - # we're before the initial fg rewrite(), so no evaluated values! - # --> use raw value instead - path_param = self._options_block.get_param('hier_block_src_path') - file_path = platform.find_file_in_paths( - filename=key + '.' + platform.get_key(), - paths=path_param.get_value(), - cwd=self.grc_file_path - ) - if file_path: # grc file found. load and get block - platform.load_and_generate_flow_graph(file_path) - block = self._get_new_block(key) # can be None + """ + Get a new block of the specified key. + Add the block to the list of elements. + + Args: + key: the block key + + Returns: + the new block or None if not found + """ + try: + block = self.platform.get_new_block(self, key) + self.blocks.append(block) + except KeyError: + block = None return block + def connect(self, porta, portb): + """ + Create a connection between porta and portb. + + Args: + porta: a port + portb: another port + @throw Exception bad connection + + Returns: + the new connection + """ + + connection = self.platform.Connection( + flow_graph=self, porta=porta, portb=portb) + self.connections.append(connection) + return connection + + def remove_element(self, element): + """ + Remove the element from the list of elements. + If the element is a port, remove the whole block. + If the element is a block, remove its connections. + If the element is a connection, just remove the connection. + """ + if element.is_port: + # Found a port, set to parent signal block + element = element.get_parent() + + if element in self.blocks: + # Remove block, remove all involved connections + for port in element.get_ports(): + map(self.remove_element, port.get_connections()) + self.blocks.remove(element) + + elif element in self.connections: + if element.is_bus(): + cons_list = [] + for i in map(lambda a: a.get_connections(), element.get_source().get_associated_ports()): + cons_list.extend(i) + map(self.remove_element, cons_list) + self.connections.remove(element) + ############################################## # Import/Export Methods ############################################## @@ -650,12 +360,12 @@ class FlowGraph(Element): a nested data odict """ # sort blocks and connections for nicer diffs - blocks = sorted(self.iter_blocks(), key=lambda b: ( + blocks = sorted(self.blocks, key=lambda b: ( b.get_key() != 'options', # options to the front not b.get_key().startswith('variable'), # then vars str(b) )) - connections = sorted(self.get_connections(), key=str) + connections = sorted(self.connections, key=str) n = odict() n['timestamp'] = self._timestamp n['block'] = [b.export_data() for b in blocks] @@ -676,11 +386,13 @@ class FlowGraph(Element): n: the nested data odict """ errors = False - self._elements = list() # remove previous elements + # Remove previous elements + del self.blocks[:] + del self.connections[:] # set file format try: instructions = n.find('_instructions') or {} - file_format = int(instructions.get('format', '0')) or self._guess_file_format_1(n) + file_format = int(instructions.get('format', '0')) or _guess_file_format_1(n) except: file_format = 0 @@ -693,6 +405,20 @@ class FlowGraph(Element): key = block_n.find('key') block = self._options_block if key == 'options' else self.get_new_block(key) + if not block: + platform = self.get_parent() + # we're before the initial fg rewrite(), so no evaluated values! + # --> use raw value instead + path_param = self._options_block.get_param('hier_block_src_path') + file_path = platform.find_file_in_paths( + filename=key + '.' + platform.get_key(), + paths=path_param.get_value(), + cwd=self.grc_file_path + ) + if file_path: # grc file found. load and get block + platform.load_and_generate_flow_graph(file_path) + block = self.get_new_block(key) # can be None + if not block: # looks like this block key cannot be found # create a dummy block instead block = self.get_new_block('dummy_block') @@ -745,40 +471,125 @@ class FlowGraph(Element): self.rewrite() # global rewrite return errors - @staticmethod - def _update_old_message_port_keys(source_key, sink_key, source_block, sink_block): - """ - Backward compatibility for message port keys + ############################################## + # Needs to go + ############################################## + def bus_ports_rewrite(self): + # todo: move to block.rewrite() + for block in self.blocks: + for direc in ['source', 'sink']: + if direc == 'source': + get_p = block.get_sources + get_p_gui = block.get_sources_gui + bus_structure = block.form_bus_structure('source') + else: + get_p = block.get_sinks + get_p_gui = block.get_sinks_gui + bus_structure = block.form_bus_structure('sink') + + if 'bus' in map(lambda a: a.get_type(), get_p_gui()): + if len(get_p_gui()) > len(bus_structure): + times = range(len(bus_structure), len(get_p_gui())) + for i in times: + for connect in get_p_gui()[-1].get_connections(): + block.get_parent().remove_element(connect) + get_p().remove(get_p_gui()[-1]) + elif len(get_p_gui()) < len(bus_structure): + n = {'name': 'bus', 'type': 'bus'} + if True in map( + lambda a: isinstance(a.get_nports(), int), + get_p()): + n['nports'] = str(1) + + times = range(len(get_p_gui()), len(bus_structure)) + + for i in times: + n['key'] = str(len(get_p())) + n = odict(n) + port = block.get_parent().get_parent().Port( + block=block, n=n, dir=direc) + get_p().append(port) + + if 'bus' in map(lambda a: a.get_type(), + block.get_sources_gui()): + for i in range(len(block.get_sources_gui())): + if len(block.get_sources_gui()[ + i].get_connections()) > 0: + source = block.get_sources_gui()[i] + sink = [] + + for j in range(len(source.get_connections())): + sink.append( + source.get_connections()[j].get_sink()) + for elt in source.get_connections(): + self.remove_element(elt) + for j in sink: + self.connect(source, j) - Message ports use their names as key (like in the 'connect' method). - Flowgraph files from former versions still have numeric keys stored for - message connections. These have to be replaced by the name of the - respective port. The correct message port is deduced from the integer - value of the key (assuming the order has not changed). - The connection ends are updated only if both ends translate into a - message port. - """ - try: - # get ports using the "old way" (assuming liner indexed keys) - source_port = source_block.get_sources()[int(source_key)] - sink_port = sink_block.get_sinks()[int(sink_key)] - if source_port.get_type() == "message" and sink_port.get_type() == "message": - source_key, sink_key = source_port.get_key(), sink_port.get_key() - except (ValueError, IndexError): - pass - return source_key, sink_key # do nothing - - @staticmethod - def _guess_file_format_1(n): - """ Try to guess the file format for flow-graph files without version tag """ - try: - has_non_numeric_message_keys = any(not ( - connection_n.find('source_key').isdigit() and - connection_n.find('sink_key').isdigit() - ) for connection_n in n.find('flow_graph').findall('connection')) - if has_non_numeric_message_keys: - return 1 - except: - pass - return 0 +def _update_old_message_port_keys(source_key, sink_key, source_block, sink_block): + """ + Backward compatibility for message port keys + + Message ports use their names as key (like in the 'connect' method). + Flowgraph files from former versions still have numeric keys stored for + message connections. These have to be replaced by the name of the + respective port. The correct message port is deduced from the integer + value of the key (assuming the order has not changed). + + The connection ends are updated only if both ends translate into a + message port. + """ + try: + # get ports using the "old way" (assuming liner indexed keys) + source_port = source_block.get_sources()[int(source_key)] + sink_port = sink_block.get_sinks()[int(sink_key)] + if source_port.get_type() == "message" and sink_port.get_type() == "message": + source_key, sink_key = source_port.get_key(), sink_port.get_key() + except (ValueError, IndexError): + pass + return source_key, sink_key # do nothing + + +def _guess_file_format_1(n): + """ + Try to guess the file format for flow-graph files without version tag + """ + try: + has_non_numeric_message_keys = any(not ( + connection_n.find('source_key').isdigit() and + connection_n.find('sink_key').isdigit() + ) for connection_n in n.find('flow_graph').findall('connection')) + if has_non_numeric_message_keys: + return 1 + except: + pass + return 0 + + +def _initialize_dummy_block(block, block_n): + """ + This is so ugly... dummy-fy a block + Modify block object to get the behaviour for a missing block + """ + + block._key = block_n.find('key') + block.is_dummy_block = lambda: True + block.is_valid = lambda: False + block.get_enabled = lambda: False + for param_n in block_n.findall('param'): + if param_n['key'] not in block.get_param_keys(): + new_param_n = odict({'key': param_n['key'], 'name': param_n['key'], 'type': 'string'}) + params = block.get_parent().get_parent().Param(block=block, n=new_param_n) + block.get_params().append(params) + + +def _dummy_block_add_port(block, key, dir): + """ This is so ugly... Add a port to a dummy-field block """ + port_n = odict({'name': '?', 'key': key, 'type': ''}) + port = block.get_parent().get_parent().Port(block=block, n=port_n, dir=dir) + if port.is_source(): + block.get_sources().append(port) + else: + block.get_sinks().append(port) + return port diff --git a/grc/model/Param.py b/grc/model/Param.py index e00be7e203..f064097256 100644 --- a/grc/model/Param.py +++ b/grc/model/Param.py @@ -121,6 +121,8 @@ class Option(Element): class Param(Element): + is_param = True + def __init__(self, block, n): """ Make a new param from nested data. @@ -666,9 +668,6 @@ class Param(Element): def get_tab_label(self): return self._tab_label - def is_param(self): - return True - def get_name(self): return self.get_parent().resolve_dependencies(self._name).strip() diff --git a/grc/model/Platform.py b/grc/model/Platform.py index c08a8edb22..f04dd04e90 100644 --- a/grc/model/Platform.py +++ b/grc/model/Platform.py @@ -19,25 +19,24 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA import os import sys - from gnuradio import gr -from .Constants import BLOCK_TREE_DTD, FLOW_GRAPH_DTD, DOMAIN_DTD -from .Element import Element - -from . import ParseXML -from .odict import odict - -from . import extract_docs +from . import ParseXML, extract_docs from .Constants import ( + BLOCK_TREE_DTD, FLOW_GRAPH_DTD, DOMAIN_DTD, HIER_BLOCKS_LIB_DIR, BLOCK_DTD, DEFAULT_FLOW_GRAPH, BLOCKS_DIRS, PREFS_FILE, CORE_TYPES, PREFS_FILE_OLD, ) -from .Generator import Generator -from .. gui import Messages +from .Element import Element +from .odict import odict +from ..gui import Messages +from .generator import Generator class Platform(Element): + + is_platform = True + def __init__(self): """ Make a platform for gnuradio. @@ -341,9 +340,6 @@ class Platform(Element): def __str__(self): return 'Platform - {}({})'.format(self.get_key(), self.get_name()) - def is_platform(self): - return True - def get_new_flow_graph(self): return self.FlowGraph(platform=self) diff --git a/grc/model/Port.py b/grc/model/Port.py index ede77353c7..bfa48102a7 100644 --- a/grc/model/Port.py +++ b/grc/model/Port.py @@ -100,6 +100,8 @@ def _get_sink_from_virtual_sink_port(vsp, traversed=[]): class Port(Element): + is_port = True + def __init__(self, block, n, dir): """ Make a new port from nested data. @@ -139,9 +141,9 @@ class Port(Element): self._clones = [] # References to cloned ports (for nports > 1) def __str__(self): - if self.is_source(): + if self.is_source: return 'Source - {}({})'.format(self.get_name(), self.get_key()) - if self.is_sink(): + if self.is_sink: return 'Sink - {}({})'.format(self.get_name(), self.get_key()) def get_types(self): @@ -180,9 +182,11 @@ class Port(Element): # Reset type and vlen self._type = '' self._vlen = '' + Element.rewrite(self) hide = self.get_parent().resolve_dependencies(self._hide).strip().lower() self._hide_evaluated = False if hide in ('false', 'off', '0') else bool(hide) + # Update domain if was deduced from (dynamic) port type type_ = self.get_type() if self._domain == GR_STREAM_DOMAIN and type_ == "message": @@ -199,7 +203,7 @@ class Port(Element): return _get_source_from_virtual_source_port(self) def resolve_empty_type(self): - if self.is_sink(): + if self.is_sink: try: src = _get_source_from_virtual_sink_port(self) if not src.is_type_empty(): @@ -209,7 +213,7 @@ class Port(Element): sink = _get_sink_from_virtual_sink_port(self) if not sink.is_type_empty(): return sink - if self.is_source(): + if self.is_source: try: src = _get_source_from_virtual_source_port(self) if not src.is_type_empty(): @@ -344,15 +348,14 @@ class Port(Element): def get_key(self): return self._key + @property def is_sink(self): return self._dir == 'sink' + @property def is_source(self): return self._dir == 'source' - def is_port(self): - return True - def get_type(self): return self.get_parent().resolve_dependencies(self._type) @@ -369,7 +372,7 @@ class Port(Element): Returns: a list of connection objects """ - connections = self.get_parent().get_parent().get_connections() + connections = self.get_parent().get_parent().connections connections = filter(lambda c: c.get_source() is self or c.get_sink() is self, connections) return connections @@ -386,7 +389,7 @@ class Port(Element): if not self.get_type() == 'bus': return [self] else: - if self.is_source(): + if self.is_source: get_ports = self.get_parent().get_sources bus_structure = self.get_parent().current_bus_structure['source'] else: diff --git a/grc/model/Generator.py b/grc/model/generator/Generator.py index f11766f910..27cb24a18b 100644 --- a/grc/model/Generator.py +++ b/grc/model/generator/Generator.py @@ -28,17 +28,19 @@ from distutils.spawn import find_executable from Cheetah.Template import Template -from . import ParseXML -from .odict import odict -from .Constants import BLOCK_FLAG_NEED_QT_GUI +from .. import ParseXML, expr_utils +from ..odict import odict -from . import expr_utils -from . Constants import ( - TOP_BLOCK_FILE_MODE, FLOW_GRAPH_TEMPLATE, +from grc.model.Constants import ( + TOP_BLOCK_FILE_MODE, BLOCK_FLAG_NEED_QT_GUI, XTERM_EXECUTABLE, HIER_BLOCK_FILE_MODE, HIER_BLOCKS_LIB_DIR, BLOCK_DTD ) -from .. gui import Messages +from grc.gui import Messages + + +DATA_DIR = os.path.dirname(__file__) +FLOW_GRAPH_TEMPLATE = os.path.join(DATA_DIR, 'flow_graph.tmpl') class Generator(object): @@ -184,7 +186,7 @@ class TopBlockGenerator(object): return code blocks = expr_utils.sort_objects( - filter(lambda b: b.get_enabled() and not b.get_bypassed(), fg.iter_blocks()), + filter(lambda b: b.get_enabled() and not b.get_bypassed(), fg.blocks), lambda b: b.get_id(), _get_block_sort_text ) # List of regular blocks (all blocks minus the special ones) diff --git a/grc/model/generator/__init__.py b/grc/model/generator/__init__.py new file mode 100644 index 0000000000..fb1e44120a --- /dev/null +++ b/grc/model/generator/__init__.py @@ -0,0 +1 @@ +from .Generator import Generator diff --git a/grc/model/flow_graph.tmpl b/grc/model/generator/flow_graph.tmpl index bd8025b676..bd8025b676 100644 --- a/grc/model/flow_graph.tmpl +++ b/grc/model/generator/flow_graph.tmpl diff --git a/grc/model/utils/__init__.py b/grc/model/utils/__init__.py new file mode 100644 index 0000000000..805d6f4aec --- /dev/null +++ b/grc/model/utils/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +"""""" + +__author__ = "Sebastian Koslowski" +__email__ = "sebastian.koslowski@gmail.com" +__copyright__ = "Copyright 2015, Sebastian Koslowski" diff --git a/grc/model/utils/complexity.py b/grc/model/utils/complexity.py new file mode 100644 index 0000000000..baa8040db4 --- /dev/null +++ b/grc/model/utils/complexity.py @@ -0,0 +1,49 @@ + +def calculate_flowgraph_complexity(flowgraph): + """ Determines the complexity of a flowgraph """ + dbal = 0 + for block in flowgraph.blocks: + # Skip options block + if block.get_key() == 'options': + continue + + # Don't worry about optional sinks? + sink_list = filter(lambda c: not c.get_optional(), block.get_sinks()) + source_list = filter(lambda c: not c.get_optional(), block.get_sources()) + sinks = float(len(sink_list)) + sources = float(len(source_list)) + base = max(min(sinks, sources), 1) + + # Port ratio multiplier + if min(sinks, sources) > 0: + multi = sinks / sources + multi = (1 / multi) if multi > 1 else multi + else: + multi = 1 + + # Connection ratio multiplier + sink_multi = max(float(sum(map(lambda c: len(c.get_connections()), sink_list)) / max(sinks, 1.0)), 1.0) + source_multi = max(float(sum(map(lambda c: len(c.get_connections()), source_list)) / max(sources, 1.0)), 1.0) + dbal = dbal + (base * multi * sink_multi * source_multi) + + blocks = float(len(flowgraph.blocks)) + connections = float(len(flowgraph.connections)) + elements = blocks + connections + disabled_connections = len(filter(lambda c: not c.get_enabled(), flowgraph.connections)) + variables = elements - blocks - connections + enabled = float(len(flowgraph.get_enabled_blocks())) + + # Disabled multiplier + if enabled > 0: + disabled_multi = 1 / (max(1 - ((blocks - enabled) / max(blocks, 1)), 0.05)) + else: + disabled_multi = 1 + + # Connection multiplier (How many connections ) + if (connections - disabled_connections) > 0: + conn_multi = 1 / (max(1 - (disabled_connections / max(connections, 1)), 0.05)) + else: + conn_multi = 1 + + final = round(max((dbal - 1) * disabled_multi * conn_multi * connections, 0.0) / 1000000, 6) + return final |