From cd99526b5d1c8b1130ac1ba87e21f96c621b1bdc Mon Sep 17 00:00:00 2001
From: Nicholas McCarthy <namccart@gmail.com>
Date: Sun, 7 Jul 2013 18:08:20 -0400
Subject: grc: add bus ports

Bus ports allow ganging together of block input or output ports into
a single display item for connection to other bus ports.
---
 grc/python/Block.py        | 31 ++++++++++++++++--
 grc/python/Connection.py   |  6 +++-
 grc/python/Constants.py    |  1 +
 grc/python/FlowGraph.py    | 80 ++++++++++++++++++++++++++++++++++++++++++++--
 grc/python/Generator.py    |  2 +-
 grc/python/Port.py         |  8 ++++-
 grc/python/block.dtd       |  6 +++-
 grc/python/convert_hier.py | 12 +++++++
 8 files changed, 137 insertions(+), 9 deletions(-)

(limited to 'grc/python')

diff --git a/grc/python/Block.py b/grc/python/Block.py
index 806de46724..d365c43319 100644
--- a/grc/python/Block.py
+++ b/grc/python/Block.py
@@ -31,6 +31,8 @@ class Block(_Block, _GUIBlock):
 	##for make sink to keep track of indexes
 	_sink_count = 0
 
+	
+
 	def __init__(self, flow_graph, n):
 		"""
 		Make a new block from nested data.
@@ -50,6 +52,8 @@ class Block(_Block, _GUIBlock):
 		self._checks = n.findall('check')
 		self._callbacks = n.findall('callback')
 		self._throttle = n.find('throttle') or ''
+		self._bus_structure_source = n.find('bus_structure_source') or ''
+		self._bus_structure_sink = n.find('bus_structure_sink') or ''
 		#build the block
 		_Block.__init__(
 			self,
@@ -58,6 +62,21 @@ class Block(_Block, _GUIBlock):
 		)
 		_GUIBlock.__init__(self)
 
+	def get_bus_structure(self, direction):
+		if direction == 'source':
+			bus_structure = self._bus_structure_source;
+		else:
+			bus_structure = self._bus_structure_sink;
+		
+		bus_structure = self.resolve_dependencies(bus_structure);
+		
+		if not bus_structure: return ''
+		try:
+			clean_bus_structure = self.get_parent().evaluate(bus_structure)
+			return clean_bus_structure
+	
+		except: return ''
+
 	def throttle(self): return bool(self._throttle)
 
 	def validate(self):
@@ -84,10 +103,11 @@ class Block(_Block, _GUIBlock):
 		def rectify(ports):
 			#restore integer contiguity after insertion
 			#rectify the port names with the index
+			self.back_ofthe_bus(ports);
 			for i, port in enumerate(ports):
 				port._key = str(i)
 				port._name = port._n['name']
-				if len(ports) > 1: port._name += str(i)
+				if len(ports) > 1 and not port._type == 'bus': port._name += str(i)
 
 		def insert_port(get_ports, get_port, key):
 			prev_port = get_port(str(int(key)-1))
@@ -121,14 +141,19 @@ class Block(_Block, _GUIBlock):
 				#remove excess ports and connections
 				if nports < num_ports:
 					for key in reversed(map(str, range(index_first+nports, index_first+num_ports))):
-						remove_port(get_ports, get_port, key)
+						remove_port(get_ports, get_port, key);
+						
+								
 					continue
 				#add more ports
 				if nports > num_ports:
 					for key in map(str, range(index_first+num_ports, index_first+nports)):
 						insert_port(get_ports, get_port, key)
+					
+							
 					continue
-
+		
+			
 	def port_controller_modify(self, direction):
 		"""
 		Change the port controller.
diff --git a/grc/python/Connection.py b/grc/python/Connection.py
index 341dd2d821..97bf61d590 100644
--- a/grc/python/Connection.py
+++ b/grc/python/Connection.py
@@ -33,7 +33,9 @@ class Connection(_Connection, _GUIConnection):
 
 	def is_message(self):
 		return self.get_source().get_type() == self.get_sink().get_type() == 'message'
-
+	
+	def is_bus(self):
+		return self.get_source().get_type() == self.get_sink().get_type() == 'bus'
 	def validate(self):
 		"""
 		Validate the connections.
@@ -44,3 +46,5 @@ class Connection(_Connection, _GUIConnection):
 		sink_size = Constants.TYPE_TO_SIZEOF[self.get_sink().get_type()] * self.get_sink().get_vlen()
 		if source_size != sink_size:
 			self.add_error_message('Source IO size "%s" does not match sink IO size "%s".'%(source_size, sink_size))
+
+	
diff --git a/grc/python/Constants.py b/grc/python/Constants.py
index b8dc9a96a1..5666a2378b 100644
--- a/grc/python/Constants.py
+++ b/grc/python/Constants.py
@@ -59,6 +59,7 @@ CORE_TYPES = ( #name, key, sizeof, color
 	('Integer 8', 's8', 1, '#FF66FF'),
 	('Message Queue', 'msg', 0, '#777777'),
 	('Async Message', 'message', 0, '#C0C0C0'),
+	('Bus Connection', 'bus', 0, '#FFFFFF'),
 	('Wildcard', '', 0, '#FFFFFF'),
 )
 
diff --git a/grc/python/FlowGraph.py b/grc/python/FlowGraph.py
index 1080006cf5..89acfd89ef 100644
--- a/grc/python/FlowGraph.py
+++ b/grc/python/FlowGraph.py
@@ -20,11 +20,17 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 import expr_utils
 from .. base.FlowGraph import FlowGraph as _FlowGraph
 from .. gui.FlowGraph import FlowGraph as _GUIFlowGraph
+from .. base.odict import odict
 import re
 
 _variable_matcher = re.compile('^(variable\w*)$')
 _parameter_matcher = re.compile('^(parameter)$')
 _monitors_searcher = re.compile('(ctrlport_monitor)')
+_bussink_searcher = re.compile('^(bus_sink)$')
+_bussrc_searcher = re.compile('^(bus_source)$')
+_bus_struct_sink_searcher = re.compile('^(bus_structure_sink)$')
+_bus_struct_src_searcher = re.compile('^(bus_structure_source)$')
+
 
 class FlowGraph(_FlowGraph, _GUIFlowGraph):
 
@@ -48,6 +54,7 @@ class FlowGraph(_FlowGraph, _GUIFlowGraph):
 		if not code: raise Exception, 'Cannot evaluate empty statement.'
 		my_hash = hash(code) ^ namespace_hash
 		#cache if does not exist
+		
 		if not self._eval_cache.has_key(my_hash):
 			self._eval_cache[my_hash] = eval(code, namespace, namespace)
 		#return from cache
@@ -69,6 +76,10 @@ class FlowGraph(_FlowGraph, _GUIFlowGraph):
 		}[direction]
         # we only want stream ports
 		sorted_pads = filter(lambda b: b.get_param('type').get_evaluated() != 'message', sorted_pads);
+		expanded_pads = [];
+		for i in sorted_pads:
+			for j in range(i.get_param('num_streams').get_evaluated()):
+				expanded_pads.append(i);
 		#load io signature
 		return [{
 			'label': str(pad.get_param('label').get_evaluated()),
@@ -76,7 +87,7 @@ class FlowGraph(_FlowGraph, _GUIFlowGraph):
 			'vlen': str(pad.get_param('vlen').get_evaluated()),
 			'size': pad.get_param('type').get_opt('size'),
 			'optional': bool(pad.get_param('optional').get_evaluated()),
-		} for pad in sorted_pads]
+		} for pad in expanded_pads]
 
 	def get_pad_sources(self):
 		"""
@@ -145,12 +156,73 @@ class FlowGraph(_FlowGraph, _GUIFlowGraph):
 		monitors = filter(lambda b: _monitors_searcher.search(b.get_key()), self.get_enabled_blocks())
                 return monitors
 
+	def get_bussink(self):
+		bussink = filter(lambda b: _bussink_searcher.search(b.get_key()), self.get_enabled_blocks())
+		
+		for i in bussink:
+			for j in i.get_params():
+				if j.get_name() == 'On/Off' and j.get_value() == 'on':
+					return True;
+			
+		return False
+		
+		
+
+	def get_bussrc(self):
+		bussrc = filter(lambda b: _bussrc_searcher.search(b.get_key()), self.get_enabled_blocks())
+
+		for i in bussrc:
+			for j in i.get_params():
+				if j.get_name() == 'On/Off' and j.get_value() == 'on':
+					return True;
+			
+		return False
+
+	def get_bus_structure_sink(self):
+		bussink = filter(lambda b: _bus_struct_sink_searcher.search(b.get_key()), self.get_enabled_blocks())
+			
+		return bussink
+
+	def get_bus_structure_src(self):
+		bussrc = filter(lambda b: _bus_struct_src_searcher.search(b.get_key()), self.get_enabled_blocks())
+		
+		return bussrc
+		
+
 	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
-		_FlowGraph.rewrite(self)
+		_FlowGraph.rewrite(self);
+		reconnect_bus_blocks();
+		
+		
+		
+		
 
 	def evaluate(self, expr):
 		"""
@@ -163,6 +235,8 @@ class FlowGraph(_FlowGraph, _GUIFlowGraph):
 		Returns:
 		    the evaluated data
 		"""
+		
+	
 		if self._renew_eval_ns:
 			self._renew_eval_ns = False
 			#reload namespace
@@ -188,6 +262,8 @@ class FlowGraph(_FlowGraph, _GUIFlowGraph):
 			#make namespace public
 			self.n = n
 			self.n_hash = hash(str(n))
+		
 		#evaluate
 		e = self._eval(expr, self.n, self.n_hash)
+		
 		return e
diff --git a/grc/python/Generator.py b/grc/python/Generator.py
index 77abc45281..47fe1d98fd 100644
--- a/grc/python/Generator.py
+++ b/grc/python/Generator.py
@@ -123,7 +123,7 @@ Add a Misc->Throttle block to your flow graph to avoid CPU congestion.''')
 		#list of regular blocks (all blocks minus the special ones)
 		blocks = filter(lambda b: b not in (imports + parameters), blocks)
 		#list of connections where each endpoint is enabled
-		connections = filter(lambda c: not (c.is_msg() or c.is_message()), self._flow_graph.get_enabled_connections())
+		connections = filter(lambda c: not (c.is_bus() or c.is_msg() or c.is_message()), self._flow_graph.get_enabled_connections())
 		messages = filter(lambda c: c.is_msg(), self._flow_graph.get_enabled_connections())
 		messages2 = filter(lambda c: c.is_message(), self._flow_graph.get_enabled_connections())
 		#list of variable names
diff --git a/grc/python/Port.py b/grc/python/Port.py
index d4afa6cf77..8ebf5c7b05 100644
--- a/grc/python/Port.py
+++ b/grc/python/Port.py
@@ -110,7 +110,8 @@ class Port(_Port, _GUIPort):
 		self._nports = n.find('nports') or ''
 		self._vlen = n.find('vlen') or ''
 		self._optional = bool(n.find('optional'))
-
+		
+	
 	def get_types(self): return Constants.TYPE_TO_SIZEOF.keys()
 
 	def is_type_empty(self): return not self._n['type']
@@ -174,6 +175,9 @@ class Port(_Port, _GUIPort):
 		try: return int(self.get_parent().get_parent().evaluate(vlen))
 		except: return 1
 
+	
+	
+
 	def get_nports(self):
 		"""
 		Get the number of ports.
@@ -191,6 +195,8 @@ class Port(_Port, _GUIPort):
 			if 0 < nports: return nports
 		except: return 1
 
+	
+
 	def get_optional(self): return bool(self._optional)
 
 	def get_color(self):
diff --git a/grc/python/block.dtd b/grc/python/block.dtd
index 292ea06cb6..99d38a0d46 100644
--- a/grc/python/block.dtd
+++ b/grc/python/block.dtd
@@ -25,7 +25,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 	Top level element.
 	A block contains a name, ...parameters list, and list of IO ports.
  -->
-<!ELEMENT block (name, key, category?, throttle?, import*, var_make?, make, callback*, param*, check*, sink*, source*, doc?, grc_source?)>
+<!ELEMENT block (name, key, category?, throttle?, import*, var_make?, make, callback*, param*, bus_sink?, bus_source?, check*, sink*, source*, bus_structure_sink?, bus_structure_source?, doc?,  grc_source?)>
 <!--
 	Sub level elements.
  -->
@@ -44,11 +44,15 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 <!ELEMENT name (#PCDATA)>
 <!ELEMENT key (#PCDATA)>
 <!ELEMENT check (#PCDATA)>
+<!ELEMENT bus_sink (#PCDATA)>
+<!ELEMENT bus_source (#PCDATA)>
 <!ELEMENT opt (#PCDATA)>
 <!ELEMENT type (#PCDATA)>
 <!ELEMENT hide (#PCDATA)>
 <!ELEMENT vlen (#PCDATA)>
 <!ELEMENT nports (#PCDATA)>
+<!ELEMENT bus_structure_sink (#PCDATA)>
+<!ELEMENT bus_structure_source (#PCDATA)>
 <!ELEMENT var_make (#PCDATA)>
 <!ELEMENT make (#PCDATA)>
 <!ELEMENT value (#PCDATA)>
diff --git a/grc/python/convert_hier.py b/grc/python/convert_hier.py
index 508ec63b2b..de76827541 100644
--- a/grc/python/convert_hier.py
+++ b/grc/python/convert_hier.py
@@ -28,6 +28,10 @@ def convert_hier(flow_graph, python_file):
 	input_msgp = flow_graph.get_msg_pad_sources();
 	output_msgp = flow_graph.get_msg_pad_sinks();
 	parameters = flow_graph.get_parameters()
+	bussink = flow_graph.get_bussink()
+	bussrc = flow_graph.get_bussrc()
+	bus_struct_sink = flow_graph.get_bus_structure_sink()
+	bus_struct_src = flow_graph.get_bus_structure_src()
 	block_key = flow_graph.get_option('id')
 	block_name = flow_graph.get_option('title') or flow_graph.get_option('id').replace('_', ' ').title()
 	block_category = flow_graph.get_option('category')
@@ -58,6 +62,10 @@ def convert_hier(flow_graph, python_file):
 		params_n.append(param_n)
 	block_n['param'] = params_n
 	#sink data stream ports
+	if bussink:
+		block_n['bus_sink'] = '1';
+	if bussrc:
+		block_n['bus_source'] = '1';
 	block_n['sink'] = list()
 	for input_sig in input_sigs:
 		sink_n = odict()
@@ -75,6 +83,10 @@ def convert_hier(flow_graph, python_file):
 		block_n['sink'].append(sink_n)
 	#source data stream ports
 	block_n['source'] = list()
+	if bus_struct_sink:
+		block_n['bus_structure_sink'] = bus_struct_sink[0].get_param('struct').get_value();
+	if bus_struct_src:
+		block_n['bus_structure_source'] = bus_struct_src[0].get_param('struct').get_value();
 	for output_sig in output_sigs:
 		source_n = odict()
 		source_n['name'] = output_sig['label']
-- 
cgit v1.2.3