diff options
author | Josh Morman <jmorman@perspectalabs.com> | 2019-06-07 15:41:26 -0400 |
---|---|---|
committer | Marcus Müller <marcus@hostalia.de> | 2019-07-15 21:48:10 +0200 |
commit | 64984004009d900944650be5dfb327150d27dc30 (patch) | |
tree | 0259e248267a769f502276e094171500bd399620 /grc | |
parent | 7fcd64e0d2287b6dc4e408c2349d53d98c0a847e (diff) |
grc: add busports back into 3.8
Bus ports had not been added back in since the refactor of grc
Hopefully this fully enables busports though there are still some
issues with the gr-fec flowgraphs
Fixes #2277
Diffstat (limited to 'grc')
-rw-r--r-- | grc/blocks/grc.tree.yml | 4 | ||||
-rw-r--r-- | grc/core/FlowGraph.py | 2 | ||||
-rw-r--r-- | grc/core/blocks/block.py | 223 | ||||
-rw-r--r-- | grc/core/generator/cpp_top_block.py | 29 | ||||
-rw-r--r-- | grc/core/generator/top_block.py | 25 | ||||
-rw-r--r-- | grc/core/ports/port.py | 54 | ||||
-rw-r--r-- | grc/gui/Application.py | 4 | ||||
-rw-r--r-- | grc/gui/canvas/block.py | 27 | ||||
-rw-r--r-- | grc/gui/canvas/connection.py | 2 | ||||
-rw-r--r-- | grc/gui/canvas/flowgraph.py | 8 | ||||
-rw-r--r-- | grc/gui/canvas/port.py | 9 |
11 files changed, 352 insertions, 35 deletions
diff --git a/grc/blocks/grc.tree.yml b/grc/blocks/grc.tree.yml index dc1067d2eb..50b08291ef 100644 --- a/grc/blocks/grc.tree.yml +++ b/grc/blocks/grc.tree.yml @@ -4,6 +4,10 @@ - pad_sink - virtual_source - virtual_sink + - bus_sink + - bus_source + - bus_structure_sink + - bus_structure_source - epy_block - epy_module - note diff --git a/grc/core/FlowGraph.py b/grc/core/FlowGraph.py index 04b73957b3..b57a93d551 100644 --- a/grc/core/FlowGraph.py +++ b/grc/core/FlowGraph.py @@ -284,6 +284,7 @@ class FlowGraph(Element): connection = self.parent_platform.Connection( parent=self, source=porta, sink=portb) self.connections.add(connection) + return connection def disconnect(self, *ports): @@ -377,6 +378,7 @@ class FlowGraph(Element): block.import_data(**block_data) + self.rewrite() # TODO: Figure out why this has to be called twice to populate bus ports correctly self.rewrite() # evaluate stuff like nports before adding connections # build the connections diff --git a/grc/core/blocks/block.py b/grc/core/blocks/block.py index 50c7873e9d..29976af282 100644 --- a/grc/core/blocks/block.py +++ b/grc/core/blocks/block.py @@ -86,9 +86,28 @@ class Block(Element): self.active_sources = [] # on rewrite self.active_sinks = [] # on rewrite - self.states = {'state': True} + self.states = {'state': True, 'bus_source': False, 'bus_sink': False, 'bus_structure': None} + if 'cpp' in self.flags: - self.orig_cpp_templates = self.cpp_templates # The original template, in case we have to edit it when transpiling to C++ + self.orig_cpp_templates = self.cpp_templates # The original template, in case we have to edit it when transpiling to C++ + + self.current_bus_structure = {'source': '', 'sink': ''} + + def get_bus_structure(self, direction): + if direction == 'source': + bus_structure = self.bus_structure_source + else: + bus_structure = self.bus_structure_sink + + if not bus_structure: + return '' # TODO: Don't like empty strings. should change this to None eventually + + try: + clean_bus_structure = self.evaluate(bus_structure) + return clean_bus_structure + except: + return '' + # region Rewrite_and_Validation def rewrite(self): @@ -112,12 +131,81 @@ class Block(Element): self._rewrite_nports(ports) rekey(ports) + self.update_bus_logic() # disconnect hidden ports self.parent_flowgraph.disconnect(*[p for p in self.ports() if p.hidden]) self.active_sources = [p for p in self.sources if not p.hidden] self.active_sinks = [p for p in self.sinks if not p.hidden] + def update_bus_logic(self): + ############################### + ## Bus Logic + ############################### + + for direc in {'source','sink'}: + if direc == 'source': + ports = self.sources + ports_gui = self.filter_bus_port(self.sources) + bus_structure = self.get_bus_structure('source') + bus_state = self.bus_source + else: + ports = self.sinks + ports_gui = self.filter_bus_port(self.sinks) + bus_structure = self.get_bus_structure('sink') + bus_state = self.bus_sink + + # Remove the bus ports + removed_bus_ports = [] + removed_bus_connections = [] + if 'bus' in map(lambda a: a.dtype, ports): + for port in ports_gui: + for c in self.parent_flowgraph.connections: + if port is c.source_port or port is c.sink_port: + removed_bus_ports.append(port) + removed_bus_connections.append(c) + ports.remove(port) + + + if (bus_state): + struct = self.form_bus_structure(direc) + self.current_bus_structure[direc] = struct + + # Hide ports that are not part of the bus structure + #TODO: Blocks where it is desired to only have a subset + # of ports included in the bus still has some issues + for idx, port in enumerate(ports): + if any([idx in bus for bus in self.current_bus_structure[direc]]): + if (port.stored_hidden_state is None): + port.stored_hidden_state = port.hidden + port.hidden = True + + # Add the Bus Ports to the list of ports + for i in range(len(struct)): + # self.sinks = [port_factory(parent=self, **params) for params in self.inputs_data] + port = self.parent.parent.make_port(self,direction=direc,id=str(len(ports)),label='bus',dtype='bus',bus_struct=struct[i]) + ports.append(port) + + for (saved_port, connection) in zip(removed_bus_ports, removed_bus_connections): + if port.key == saved_port.key: + self.parent_flowgraph.connections.remove(connection) + if saved_port.is_source: + connection.source_port = port + if saved_port.is_sink: + connection.sink_port = port + self.parent_flowgraph.connections.add(connection) + + + else: + self.current_bus_structure[direc] = None + + # Re-enable the hidden property of the ports + for port in ports: + port.hidden = port.stored_hidden_state + port.stored_hidden_state = None + + + def _rewrite_nports(self, ports): for port in ports: if hasattr(port, 'master_port'): # Not a master port and no left-over clones @@ -238,6 +326,44 @@ class Block(Element): """Get the enabled state of the block""" return self.state != 'disabled' + @property + def bus_sink(self): + """Gets the block's current Toggle Bus Sink state.""" + return self.states['bus_sink'] + + @bus_sink.setter + def bus_sink(self, value): + """Sets the Toggle Bus Sink state for the block.""" + self.states['bus_sink'] = value + + @property + def bus_source(self): + """Gets the block's current Toggle Bus Sink state.""" + return self.states['bus_source'] + + @bus_source.setter + def bus_source(self, value): + """Sets the Toggle Bus Source state for the block.""" + self.states['bus_source'] = value + + @property + def bus_structure_source(self): + """Gets the block's current source bus structure.""" + try: + bus_structure = self.params['bus_structure_source'].value or None + except: + bus_structure = None + return bus_structure + + @property + def bus_structure_sink(self): + """Gets the block's current source bus structure.""" + try: + bus_structure = self.params['bus_structure_sink'].value or None + except: + bus_structure = None + return bus_structure + # endregion ############################################## @@ -461,3 +587,96 @@ class Block(Element): # Store hash and call rewrite pre_rewrite_hash = get_hash() self.rewrite() + + ############################################## + # Controller Modify + ############################################## + def filter_bus_port(self, ports): + buslist = [p for p in ports if p.dtype == 'bus'] + return buslist or ports + + def type_controller_modify(self, direction): + """ + Change the type controller. + + Args: + direction: +1 or -1 + + Returns: + true for change + """ + changed = False + type_param = None + for param in filter(lambda p: p.is_enum(), self.get_params()): + children = self.get_ports() + self.get_params() + # Priority to the type controller + if param.get_key() in ' '.join(map(lambda p: p._type, children)): type_param = param + # Use param if type param is unset + if not type_param: + type_param = param + if type_param: + # Try to increment the enum by direction + try: + keys = type_param.get_option_keys() + old_index = keys.index(type_param.get_value()) + new_index = (old_index + direction + len(keys)) % len(keys) + type_param.set_value(keys[new_index]) + changed = True + except: + pass + return changed + + def form_bus_structure(self, direc): + if direc == 'source': + ports = self.sources + bus_structure = self.get_bus_structure('source') + else: + ports = self.sinks + bus_structure = self.get_bus_structure('sink') + + struct = [range(len(ports))] + # struct = list(range(len(ports))) + #TODO for more complicated port structures, this code is needed but not working yet + if any([p.multiplicity for p in ports]): + structlet = [] + last = 0 + # group the ports with > n inputs together on the bus + cnt = 0 + idx = 0 + for p in ports: + if cnt > 0: + cnt -= 1 + continue + + if p.multiplicity > 1: + cnt = p.multiplicity-1 + structlet.append([idx+j for j in range(p.multiplicity)]) + else: + structlet.append([idx]) + + struct = structlet + if bus_structure: + struct = bus_structure + + self.current_bus_structure[direc] = struct + return struct + + def bussify(self, direc): + if direc == 'source': + ports = self.sources + ports_gui = self.filter_bus_port(self.sources) + self.bus_structure = self.get_bus_structure('source') + self.bus_source = not self.bus_source + else: + ports = self.sinks + ports_gui = self.filter_bus_port(self.sinks) + self.bus_structure = self.get_bus_structure('sink') + self.bus_sink = not self.bus_sink + + # Disconnect all the connections when toggling the bus state + for port in ports: + l_connections = list(port.connections()) + for connect in l_connections: + self.parent.remove_element(connect) + + self.update_bus_logic() diff --git a/grc/core/generator/cpp_top_block.py b/grc/core/generator/cpp_top_block.py index 4e668f25ce..d7b9c80ed4 100644 --- a/grc/core/generator/cpp_top_block.py +++ b/grc/core/generator/cpp_top_block.py @@ -353,9 +353,30 @@ class CppTopBlockGenerator(TopBlockGenerator): rendered = [] for con in sorted(connections, key=by_domain_and_blocks): template = templates[con.type] - code = template.render(make_port_sig=make_port_sig, source=con.source_port, sink=con.sink_port) - if not self._generate_options.startswith('hb'): - code = 'this->tb->' + code - rendered.append(code) + + if con.source_port.dtype != 'bus': + code = template.render(make_port_sig=make_port_sig, source=con.source_port, sink=con.sink_port) + if not self._generate_options.startswith('hb'): + code = 'this->tb->' + code + rendered.append(code) + else: + # Bus ports need to iterate over the underlying connections and then render + # the code for each subconnection + porta = con.source_port + portb = con.sink_port + fg = self._flow_graph + + if porta.dtype == 'bus' and portb.dtype == 'bus': + # which bus port is this relative to the bus structure + if len(porta.bus_structure) == len(portb.bus_structure): + for port_num in porta.bus_structure: + hidden_porta = porta.parent.sources[port_num] + hidden_portb = portb.parent.sinks[port_num] + connection = fg.parent_platform.Connection( + parent=self, source=hidden_porta, sink=hidden_portb) + code = template.render(make_port_sig=make_port_sig, source=hidden_porta, sink=hidden_portb) + if not self._generate_options.startswith('hb'): + code = 'this->tb->' + code + rendered.append(code) return rendered diff --git a/grc/core/generator/top_block.py b/grc/core/generator/top_block.py index d449d1baa9..32e8574aed 100644 --- a/grc/core/generator/top_block.py +++ b/grc/core/generator/top_block.py @@ -298,7 +298,28 @@ class TopBlockGenerator(object): rendered = [] for con in sorted(connections, key=by_domain_and_blocks): template = templates[con.type] - code = template.render(make_port_sig=make_port_sig, source=con.source_port, sink=con.sink_port) - rendered.append(code) + if con.source_port.dtype != 'bus': + code = template.render(make_port_sig=make_port_sig, source=con.source_port, sink=con.sink_port) + rendered.append(code) + else: + # Bus ports need to iterate over the underlying connections and then render + # the code for each subconnection + porta = con.source_port + portb = con.sink_port + fg = self._flow_graph + + if porta.dtype == 'bus' and portb.dtype == 'bus': + # which bus port is this relative to the bus structure + if len(porta.bus_structure) == len(portb.bus_structure): + for port_num_a,port_num_b in zip(porta.bus_structure,portb.bus_structure): + hidden_porta = porta.parent.sources[port_num_a] + hidden_portb = portb.parent.sinks[port_num_b] + connection = fg.parent_platform.Connection( + parent=self, source=hidden_porta, sink=hidden_portb) + code = template.render(make_port_sig=make_port_sig, source=hidden_porta, sink=hidden_portb) + rendered.append(code) + + + return rendered diff --git a/grc/core/ports/port.py b/grc/core/ports/port.py index d1a32e915c..dcaca5cbc5 100644 --- a/grc/core/ports/port.py +++ b/grc/core/ports/port.py @@ -39,7 +39,7 @@ class Port(Element): optional = EvaluatedFlag() def __init__(self, parent, direction, id, label='', domain=Constants.DEFAULT_DOMAIN, dtype='', - vlen='', multiplicity=1, optional=False, hide=False, **_): + vlen='', multiplicity=1, optional=False, hide=False, bus_struct=None, **_): """Make a new port from nested data.""" Element.__init__(self, parent) @@ -47,6 +47,14 @@ class Port(Element): self.key = id if not label: label = id if not id.isdigit() else {'sink': 'in', 'source': 'out'}[direction] + if dtype == 'bus': + #TODO - add some error checking in here + busses = [p for p in self.parent.ports() if p._dir == self._dir and p.dtype == 'bus'] + bus_structure = self.parent.current_bus_structure[self._dir] + bus_index = len(busses) + number = str(len(busses)) + '#' + str(len(bus_structure[bus_index])) + label = dtype + number + self.name = self._base_name = label self.domain = domain @@ -60,6 +68,9 @@ class Port(Element): self.multiplicity = multiplicity self.optional = optional self.hidden = hide + self.stored_hidden_state = None + self.bus_structure = bus_struct + # end of args ######################################################## self.clones = [] # References to cloned ports (for nports > 1) @@ -203,5 +214,42 @@ class Port(Element): enabled: None for all, True for enabled only, False for disabled only """ for con in self.parent_flowgraph.connections: - if self in con and (enabled is None or enabled == con.enabled): - yield con + #TODO clean this up - but how to get past this validation + # things don't compare simply with an x in y because + # bus ports are created differently. + port_in_con = False + if self.dtype == 'bus': + if self.is_sink: + if (self.parent.name == con.sink_port.parent.name and + self.name == con.sink_port.name): + port_in_con = True + elif self.is_source: + if (self.parent.name == con.source_port.parent.name and + self.name == con.source_port.name): + port_in_con = True + + if port_in_con: + yield con + + else: + if self in con and (enabled is None or enabled == con.enabled): + yield con + + def get_associated_ports(self): + if not self.dtype == 'bus': + return [self] + else: + if self.is_source: + get_ports = self.parent.sources + bus_structure = self.parent.current_bus_structure['source'] + else: + get_ports = self.parent.sinks + bus_structure = self.parent.current_bus_structure['sink'] + + ports = [i for i in get_ports if not i.dtype == 'bus'] + if bus_structure: + busses = [i for i in get_ports if i.dtype == 'bus'] + bus_index = busses.index(self) + ports = filter(lambda a: ports.index(a) in bus_structure[bus_index], ports) + return ports + diff --git a/grc/gui/Application.py b/grc/gui/Application.py index 8489580b06..fff663177b 100644 --- a/grc/gui/Application.py +++ b/grc/gui/Application.py @@ -802,8 +802,8 @@ class Application(Gtk.Application): Actions.BLOCK_CREATE_HIER.set_enabled(bool(selected_blocks)) Actions.OPEN_HIER.set_enabled(bool(selected_blocks)) - #Actions.BUSSIFY_SOURCES.set_enabled(bool(selected_blocks)) - #Actions.BUSSIFY_SINKS.set_enabled(bool(selected_blocks)) + Actions.BUSSIFY_SOURCES.set_enabled(bool(selected_blocks)) + Actions.BUSSIFY_SINKS.set_enabled(bool(selected_blocks)) Actions.RELOAD_BLOCKS.enable() Actions.FIND_BLOCKS.enable() diff --git a/grc/gui/canvas/block.py b/grc/gui/canvas/block.py index 31a474fae2..e55c76f2cf 100644 --- a/grc/gui/canvas/block.py +++ b/grc/gui/canvas/block.py @@ -128,16 +128,14 @@ class Block(CoreBlock, Drawable): self._area = (0, 0, self.height, self.width) self.bounds_from_area(self._area) - # bussified = self.current_bus_structure['source'], self.current_bus_structure['sink'] - bussified = False, False + bussified = self.current_bus_structure['source'], self.current_bus_structure['sink'] for ports, has_busses in zip((self.active_sources, self.active_sinks), bussified): if not ports: continue port_separation = PORT_SEPARATION if not has_busses else ports[0].height + PORT_SPACING offset = (self.height - (len(ports) - 1) * port_separation - ports[0].height) / 2 for port in ports: - port.create_shapes() - + port.create_shapes() port.coordinate = { 0: (+self.width, offset), 90: (offset, -port.width), @@ -193,24 +191,21 @@ class Block(CoreBlock, Drawable): def get_min_height_for_ports(ports): min_height = 2 * PORT_BORDER_SEPARATION + len(ports) * PORT_SEPARATION - if ports: - min_height -= ports[-1].height + # If any of the ports are bus ports - make the min height larger + if any([p.dtype == 'bus' for p in ports]): + min_height = 2 * PORT_BORDER_SEPARATION + sum( + port.height + PORT_SPACING for port in ports if port.dtype == 'bus' + ) - PORT_SPACING + + else: + if ports: + min_height -= ports[-1].height return min_height height = max(height, get_min_height_for_ports(self.active_sinks), get_min_height_for_ports(self.active_sources)) - # def get_min_height_for_bus_ports(ports): - # return 2 * PORT_BORDER_SEPARATION + sum( - # port.height + PORT_SPACING for port in ports if port.dtype == 'bus' - # ) - PORT_SPACING - # - # if self.current_bus_structure['sink']: - # height = max(height, get_min_height_for_bus_ports(self.active_sinks)) - # if self.current_bus_structure['source']: - # height = max(height, get_min_height_for_bus_ports(self.active_sources)) - self.width, self.height = width, height = Utils.align_to_grid((width, height)) self._surface_layouts_offsets = [ diff --git a/grc/gui/canvas/connection.py b/grc/gui/canvas/connection.py index 56dab45570..2accfaf4d6 100644 --- a/grc/gui/canvas/connection.py +++ b/grc/gui/canvas/connection.py @@ -113,7 +113,7 @@ class Connection(CoreConnection, Drawable): self._make_path() # no cr set --> only sets bounding_points for extent def _make_path(self, cr=None): - x_pos, y_pos = self.coordinate # is source connector coordinate + x_pos, y_pos = self.source_port.connector_coordinate_absolute # x_start, y_start = self.source_port.get_connector_coordinate() x_end, y_end = self.sink_port.connector_coordinate_absolute diff --git a/grc/gui/canvas/flowgraph.py b/grc/gui/canvas/flowgraph.py index ca27406e0e..5bdda4745e 100644 --- a/grc/gui/canvas/flowgraph.py +++ b/grc/gui/canvas/flowgraph.py @@ -487,7 +487,13 @@ class FlowGraph(CoreFlowgraph, Drawable): element.create_labels(cr) def create_shapes(self): - for element in self._elements_to_draw: + #TODO - this is a workaround for bus ports not having a proper coordinate + # until the shape is drawn. The workaround is to draw blocks before connections + + for element in filter(lambda x: x.is_block, self._elements_to_draw) : + element.create_shapes() + + for element in filter(lambda x: not x.is_block, self._elements_to_draw): element.create_shapes() def _drawables(self): diff --git a/grc/gui/canvas/port.py b/grc/gui/canvas/port.py index 2ea35f3dd3..b1a3867149 100644 --- a/grc/gui/canvas/port.py +++ b/grc/gui/canvas/port.py @@ -124,17 +124,18 @@ class Port(CorePort, Drawable): label_width, label_height = self.label_layout.get_size() self.width = 2 * Constants.PORT_LABEL_PADDING + label_width / Pango.SCALE - self.height = 2 * Constants.PORT_LABEL_PADDING + label_height / Pango.SCALE + self.height = (2 * Constants.PORT_LABEL_PADDING + label_height*(3 if self.dtype == 'bus' else 1) ) / Pango.SCALE self._label_layout_offsets = [0, Constants.PORT_LABEL_PADDING] - # if self.dtype == 'bus': - # self.height += Constants.PORT_EXTRA_BUS_HEIGHT - # self._label_layout_offsets[1] += Constants.PORT_EXTRA_BUS_HEIGHT / 2 + self.height += self.height % 2 # uneven height def draw(self, cr): """ Draw the socket with a label. """ + if self.hidden: + return + border_color = self._border_color cr.set_line_width(self._line_width_factor * cr.get_line_width()) cr.translate(*self.coordinate) |