summaryrefslogtreecommitdiff
path: root/grc/python/Generator.py
diff options
context:
space:
mode:
authorJohnathan Corgan <johnathan@corganlabs.com>2014-12-09 11:42:00 -0800
committerJohnathan Corgan <johnathan@corganlabs.com>2014-12-09 11:42:00 -0800
commitcab562941fc56e2a72f22e83f630c670d7c52ffe (patch)
treeaa4e3440adf479960c57258913d8499f7186cdd6 /grc/python/Generator.py
parent0463962872ed459aa5d4894f15f0ad9a7e149c83 (diff)
parentac726bdd0aac71cbd85c7dcb11c718cd7e2732d7 (diff)
Merge remote-tracking branch 'gnuradio-wg-grc/master_grcwg'
Diffstat (limited to 'grc/python/Generator.py')
-rw-r--r--grc/python/Generator.py214
1 files changed, 180 insertions, 34 deletions
diff --git a/grc/python/Generator.py b/grc/python/Generator.py
index 4a496b7c0f..24d943d28d 100644
--- a/grc/python/Generator.py
+++ b/grc/python/Generator.py
@@ -23,14 +23,18 @@ import subprocess
import tempfile
from distutils.spawn import find_executable
from Cheetah.Template import Template
-import expr_utils
-from Constants import \
- TOP_BLOCK_FILE_MODE, HIER_BLOCK_FILE_MODE, HIER_BLOCKS_LIB_DIR, FLOW_GRAPH_TEMPLATE, XTERM_EXECUTABLE
-import convert_hier
+
from .. gui import Messages
+from .. base import ParseXML
+from .. base import odict
+
+from . Constants import TOP_BLOCK_FILE_MODE, FLOW_GRAPH_TEMPLATE, \
+ XTERM_EXECUTABLE, HIER_BLOCK_FILE_MODE, HIER_BLOCKS_LIB_DIR, BLOCK_DTD
+from . import expr_utils
class Generator(object):
+ """Adaptor for various generators (uses generate_options)"""
def __init__(self, flow_graph, file_path):
"""
@@ -39,44 +43,67 @@ class Generator(object):
Args:
flow_graph: the flow graph object
+ file_path: the path to the grc file
+ """
+ self._generate_options = flow_graph.get_option('generate_options')
+ if self._generate_options == 'hb':
+ self._generator = HierBlockGenerator(flow_graph, file_path)
+ else:
+ self._generator = TopBlockGenerator(flow_graph, file_path)
+
+ def get_generate_options(self):
+ return self._generate_options
+
+ def __getattr__(self, item):
+ """get all other attrib from actual generator object"""
+ return getattr(self._generator, item)
+
+
+class TopBlockGenerator(object):
+
+ def __init__(self, flow_graph, file_path):
+ """
+ Initialize the top block generator object.
+
+ Args:
+ flow_graph: the flow graph object
file_path: the path to write the file to
"""
self._flow_graph = flow_graph
self._generate_options = self._flow_graph.get_option('generate_options')
- if self._generate_options == 'hb':
- self._mode = HIER_BLOCK_FILE_MODE
- dirname = HIER_BLOCKS_LIB_DIR
- else:
- self._mode = TOP_BLOCK_FILE_MODE
- dirname = os.path.dirname(file_path)
- #handle the case where the directory is read-only
- #in this case, use the system's temp directory
- if not os.access(dirname, os.W_OK):
- dirname = tempfile.gettempdir()
+ self._mode = TOP_BLOCK_FILE_MODE
+ dirname = os.path.dirname(file_path)
+ #handle the case where the directory is read-only
+ #in this case, use the system's temp directory
+ if not os.access(dirname, os.W_OK):
+ dirname = tempfile.gettempdir()
filename = self._flow_graph.get_option('id') + '.py'
self._file_path = os.path.join(dirname, filename)
- def get_file_path(self): return self._file_path
+ def get_file_path(self):
+ return self._file_path
def write(self):
+ """generate output and write it to files"""
#do throttle warning
throttling_blocks = filter(lambda b: b.throttle(), self._flow_graph.get_enabled_blocks())
if not throttling_blocks and self._generate_options != 'hb':
- Messages.send_warning('''\
-This flow graph may not have flow control: no audio or RF hardware blocks found. \
-Add a Misc->Throttle block to your flow graph to avoid CPU congestion.''')
+ Messages.send_warning("This flow graph may not have flow control: "
+ "no audio or RF hardware blocks found. "
+ "Add a Misc->Throttle block to your flow "
+ "graph to avoid CPU congestion.")
if len(throttling_blocks) > 1:
keys = set(map(lambda b: b.get_key(), throttling_blocks))
if len(keys) > 1 and 'blocks_throttle' in keys:
- Messages.send_warning('''\
-This flow graph contains a throttle block and another rate limiting block, e.g. a hardware source or sink. \
-This is usually undesired. Consider removing the throttle block.''')
+ Messages.send_warning("This flow graph contains a throttle "
+ "block and another rate limiting block, "
+ "e.g. a hardware source or sink. "
+ "This is usually undesired. Consider "
+ "removing the throttle block.")
#generate
- open(self.get_file_path(), 'w').write(str(self))
- if self._generate_options == 'hb':
- #convert hier block to xml wrapper
- convert_hier.convert_hier(self._flow_graph, self.get_file_path())
- os.chmod(self.get_file_path(), self._mode)
+ open(self.get_file_path(), 'w').write(
+ self._build_python_code_from_template()
+ )
def get_popen(self):
"""
@@ -101,10 +128,12 @@ This is usually undesired. Consider removing the throttle block.''')
if self._generate_options == 'no_gui' and xterm_executable:
cmds = [xterm_executable, '-e'] + cmds
- p = subprocess.Popen(args=cmds, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=False, universal_newlines=True)
+ p = subprocess.Popen(
+ args=cmds, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
+ shell=False, universal_newlines=True)
return p
- def __str__(self):
+ def _build_python_code_from_template(self):
"""
Convert the flow graph to python code.
@@ -119,10 +148,14 @@ This is usually undesired. Consider removing the throttle block.''')
#list of blocks not including variables and imports and parameters and disabled
def _get_block_sort_text(block):
code = block.get_make().replace(block.get_id(), ' ')
- try: code += block.get_param('notebook').get_value() #older gui markup w/ wxgui
- except: pass
- try: code += block.get_param('gui_hint').get_value() #newer gui markup w/ qtgui
- except: pass
+ try:
+ code += block.get_param('notebook').get_value() #older gui markup w/ wxgui
+ except:
+ pass
+ try:
+ code += block.get_param('gui_hint').get_value() #newer gui markup w/ qtgui
+ except:
+ pass
return code
blocks = expr_utils.sort_objects(
self._flow_graph.get_enabled_blocks(),
@@ -137,7 +170,7 @@ This is usually undesired. Consider removing the throttle block.''')
#list of variable names
var_ids = [var.get_id() for var in parameters + variables]
#prepend self.
- replace_dict = dict([(var_id, 'self.%s'%var_id) for var_id in var_ids])
+ replace_dict = dict([(var_id, 'self.%s' % var_id) for var_id in var_ids])
#list of callbacks
callbacks = [
expr_utils.expr_replace(cb, replace_dict)
@@ -155,7 +188,7 @@ This is usually undesired. Consider removing the throttle block.''')
'flow_graph': self._flow_graph,
'variables': variables,
'parameters': parameters,
- 'monitors': monitors,
+ 'monitors': monitors,
'blocks': blocks,
'connections': connections,
'messages': messages,
@@ -166,3 +199,116 @@ This is usually undesired. Consider removing the throttle block.''')
#build the template
t = Template(open(FLOW_GRAPH_TEMPLATE, 'r').read(), namespace)
return str(t)
+
+
+class HierBlockGenerator(TopBlockGenerator):
+ """Extends the top block generator to also generate a block XML file"""
+
+ def __init__(self, flow_graph, file_path):
+ """
+ Initialize the hier block generator object.
+
+ Args:
+ flow_graph: the flow graph object
+ file_path: where to write the py file (the xml goes into HIER_BLOCK_LIB_DIR)
+ """
+ TopBlockGenerator.__init__(self, flow_graph, file_path)
+ self._mode = HIER_BLOCK_FILE_MODE
+ self._file_path = os.path.join(HIER_BLOCKS_LIB_DIR,
+ self._flow_graph.get_option('id') + '.py')
+ self._file_path_xml = self._file_path + '.xml'
+
+ def get_file_path_xml(self):
+ return self._file_path_xml
+
+ def write(self):
+ """generate output and write it to files"""
+ TopBlockGenerator.write(self)
+ ParseXML.to_file(self._build_block_n_from_flow_graph_io(), self.get_file_path_xml())
+ ParseXML.validate_dtd(self.get_file_path_xml(), BLOCK_DTD)
+
+ def _build_block_n_from_flow_graph_io(self):
+ """
+ Generate a block XML nested data from the flow graph IO
+
+ Returns:
+ a xml node tree
+ """
+ #extract info from the flow graph
+ block_key = self._flow_graph.get_option('id')
+ parameters = self._flow_graph.get_parameters()
+
+ def var_or_value(name):
+ if name in map(lambda p: p.get_id(), parameters):
+ return "$"+name
+ return name
+
+ #build the nested data
+ block_n = odict()
+ block_n['name'] = self._flow_graph.get_option('title') or \
+ self._flow_graph.get_option('id').replace('_', ' ').title()
+ block_n['key'] = block_key
+ block_n['category'] = self._flow_graph.get_option('category')
+ block_n['import'] = 'execfile("{0}")'.format(self.get_file_path())
+ #make data
+ if parameters:
+ block_n['make'] = '{cls}(\n {kwargs},\n)'.format(
+ cls=block_key,
+ kwargs=',\n '.join(
+ '{key}=${key}'.format(key=param.get_id()) for param in parameters
+ ),
+ )
+ else:
+ block_n['make'] = '{cls}()'.format(cls=block_key)
+ #callback data
+ block_n['callback'] = [
+ 'set_{key}(${key})'.format(key=param.get_id()) for param in parameters
+ ]
+
+ # Parameters
+ block_n['param'] = list()
+ for param in parameters:
+ param_n = odict()
+ param_n['name'] = param.get_param('label').get_value() or param.get_id()
+ param_n['key'] = param.get_id()
+ param_n['value'] = param.get_param('value').get_value()
+ param_n['type'] = 'raw'
+ block_n['param'].append(param_n)
+
+ # bus stuff
+ if self._flow_graph.get_bussink():
+ block_n['bus_sink'] = '1'
+ if self._flow_graph.get_bussrc():
+ block_n['bus_source'] = '1'
+
+ # sink/source ports
+ for direction in ('sink', 'source'):
+ block_n[direction] = list()
+ for port in self._flow_graph.get_hier_block_io(direction):
+ port_n = odict()
+ port_n['name'] = port['label']
+ port_n['type'] = port['type']
+ if port['type'] != "message":
+ port_n['vlen'] = var_or_value(port['vlen'])
+ if port['optional']:
+ port_n['optional'] = '1'
+ block_n[direction].append(port_n)
+
+ # more bus stuff
+ bus_struct_sink = self._flow_graph.get_bus_structure_sink()
+ if bus_struct_sink:
+ block_n['bus_structure_sink'] = bus_struct_sink[0].get_param('struct').get_value()
+ bus_struct_src = self._flow_graph.get_bus_structure_src()
+ if bus_struct_src:
+ block_n['bus_structure_source'] = bus_struct_src[0].get_param('struct').get_value()
+
+ # documentation
+ block_n['doc'] = "\n".join(field for field in (
+ self._flow_graph.get_option('author'),
+ self._flow_graph.get_option('description'),
+ self.get_file_path()
+ ) if field)
+ block_n['grc_source'] = str(self._flow_graph.grc_file_path)
+
+ n = {'block': block_n}
+ return n