Added C++ support to gr-analog, gr-blocks and grc

This commit is contained in:
Håkon Vågsether 2017-12-21 00:07:28 +01:00
parent be8dafb747
commit 4fc8dcef66
20 changed files with 112 additions and 83 deletions

View File

@ -1,5 +1,6 @@
id: import_ id: import_
label: Import label: Import
flags: python
parameters: parameters:
- id: imports - id: imports

View File

@ -1,5 +1,6 @@
id: note id: note
label: Note label: Note
flags: python, cpp
parameters: parameters:
- id: note - id: note

View File

@ -1,6 +1,6 @@
id: options id: options
label: Options label: Options
flags: has_python, has_cpp flags: python, cpp
parameters: parameters:
- id: title - id: title
@ -156,7 +156,7 @@ templates:
else: self.stop(); self.wait()' else: self.stop(); self.wait()'
cpp_templates: cpp_templates:
includes: '#include <gnuradio/top_block.h>' includes: ['#include <gnuradio/top_block.h>']
documentation: |- documentation: |-
The options block sets special parameters for the flow graph. Only one option block is allowed per flow graph. The options block sets special parameters for the flow graph. Only one option block is allowed per flow graph.

View File

@ -1,5 +1,6 @@
id: pad_sink id: pad_sink
label: Pad Sink label: Pad Sink
flags: python
parameters: parameters:
- id: label - id: label

View File

@ -1,5 +1,6 @@
id: pad_source id: pad_source
label: Pad Source label: Pad Source
flags: python
parameters: parameters:
- id: label - id: label

View File

@ -1,5 +1,6 @@
id: parameter id: parameter
label: Parameter label: Parameter
flags: python, cpp
parameters: parameters:
- id: label - id: label
@ -37,6 +38,10 @@ templates:
var_make: self.${id} = ${id} var_make: self.${id} = ${id}
make: ${value} make: ${value}
cpp_templates:
var_make: ${type.type} ${id} = ${id};
make: ${value}
documentation: |- documentation: |-
This block represents a parameter to the flow graph. A parameter can be used to pass command line arguments into a top block. Or, parameters can pass arguments into an instantiated hierarchical block. This block represents a parameter to the flow graph. A parameter can be used to pass command line arguments into a top block. Or, parameters can pass arguments into an instantiated hierarchical block.

View File

@ -7,5 +7,5 @@ multiple_connections_per_output: true
templates: templates:
- type: [stream, stream] - type: [stream, stream]
connect: connect(${ make_port_sig(source) }, ${ make_port_sig(sink) }) connect: self.connect(${ make_port_sig(source) }, ${ make_port_sig(sink) })
cpp_connect: hier_block2::connect(${ make_port_sig(source) }, ${ make_port_sig(sink) }) cpp_connect: hier_block2::connect(${ make_port_sig(source) }, ${ make_port_sig(sink) })

View File

@ -1,6 +1,6 @@
id: variable id: variable
label: Variable label: Variable
flags: has_python, has_cpp flags: python, cpp
parameters: parameters:
- id: value - id: value

View File

@ -1,5 +1,6 @@
id: variable_config id: variable_config
label: Variable Config label: Variable Config
flags: python
parameters: parameters:
- id: value - id: value

View File

@ -1,5 +1,6 @@
id: variable_function_probe id: variable_function_probe
label: Function Probe label: Function Probe
flags: python
parameters: parameters:
- id: block_id - id: block_id

View File

@ -25,8 +25,8 @@ class Flags(object):
NEED_QT_GUI = 'need_qt_gui' NEED_QT_GUI = 'need_qt_gui'
DEPRECATED = 'deprecated' DEPRECATED = 'deprecated'
NOT_DSP = 'not_dsp' NOT_DSP = 'not_dsp'
HAS_PYTHON = 'has_python' HAS_PYTHON = 'python'
HAS_CPP = 'has_cpp' HAS_CPP = 'cpp'
def __init__(self, flags): def __init__(self, flags):
self.data = set(flags) self.data = set(flags)

View File

@ -169,9 +169,14 @@ class Block(Element):
"""check if this block supports the selected output language""" """check if this block supports the selected output language"""
current_output_language = self.parent.get_option('output_language') current_output_language = self.parent.get_option('output_language')
if current_output_language == 'cpp' and 'has_cpp' not in self.flags: if current_output_language == 'cpp':
if 'cpp' not in self.flags:
self.add_error_message("This block does not support C++ output.") self.add_error_message("This block does not support C++ output.")
if self.key == 'parameter':
if not self.params['type'].value:
self.add_error_message("C++ output requires you to choose a parameter type.")
def _validate_var_value(self): def _validate_var_value(self):
"""or variables check the value (only if var_value is used)""" """or variables check the value (only if var_value is used)"""
if self.is_variable and self.value != 'value': if self.is_variable and self.value != 'value':
@ -266,6 +271,8 @@ class Block(Element):
a list of strings a list of strings
""" """
def make_callback(callback): def make_callback(callback):
if self.is_variable:
return callback
if 'this->' in callback: if 'this->' in callback:
return callback return callback
return 'this->{}->{}'.format(self.name, callback) return 'this->{}->{}'.format(self.name, callback)

View File

@ -16,7 +16,7 @@ class_name = flow_graph.get_option('id')
cmake_opt_list = flow_graph.get_option('cmake_opt').split(";") cmake_opt_list = flow_graph.get_option('cmake_opt').split(";")
%>\ %>\
# cmake_minimum_required(VERSION 3.8) Which version? cmake_minimum_required(VERSION 3.8) # Which version?
% if generate_options == 'qt_gui': % if generate_options == 'qt_gui':
find_package(Qt5Widgets REQUIRED) find_package(Qt5Widgets REQUIRED)
@ -50,11 +50,14 @@ set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
set(GR_LIBRARIES set(GR_LIBRARIES
boost_system boost_system
% if parameters:
boost_program_options
% endif
gnuradio-blocks gnuradio-blocks
gnuradio-runtime gnuradio-runtime
gnuradio-pmt gnuradio-pmt
% for link in links: % for link in links:
% if link != '': % if link:
${link} ${link}
% endif % endif
% endfor % endfor

View File

@ -1,3 +1,4 @@
<%def name="doubleindent(code)">${ '\n '.join(str(code).splitlines()) }</%def>\
/******************** /********************
GNU Radio C++ Flow Graph Source File GNU Radio C++ Flow Graph Source File
@ -12,21 +13,24 @@ Generated: ${generated_time}
********************/ ********************/
#include "${flow_graph.get_option('id')}.hpp" #include "${flow_graph.get_option('id')}.hpp"
% if parameters:
namespace po = boost::program_options;
% endif
using namespace gr; using namespace gr;
<% <%
class_name = flow_graph.get_option('id') class_name = flow_graph.get_option('id')
## TODO: param_str param_str = ", ".join((param.vtype + " " + param.name) for param in parameters)
param_str_without_types = ", ".join(param.name for param in parameters)
%>\ %>\
<%def name="indent(code)">${ '\n '.join(str(code).splitlines()) }</%def>
## TODO: param_str
% if generate_options == 'no_gui': % if generate_options == 'no_gui':
${class_name}::${class_name} () : top_block("${title}") { ${class_name}::${class_name} (${param_str}) {
% elif generate_options.startswith('hb'): % elif generate_options.startswith('hb'):
## TODO: make_io_sig ## TODO: make_io_sig
${class_name}::${class_name} () : hier_block2("${title}") { ${class_name}::${class_name} (${param_str}) : hier_block2("${title}") {
% for pad in flow_graph.get_hier_block_message_io('in'): % for pad in flow_graph.get_hier_block_message_io('in'):
message_port_register_hier_in("${pad['label']}") message_port_register_hier_in("${pad['label']}")
% endfor % endfor
@ -34,7 +38,7 @@ ${class_name}::${class_name} () : hier_block2("${title}") {
message_port_register_hier_out("${pad['label']}") message_port_register_hier_out("${pad['label']}")
% endfor % endfor
% elif generate_options == 'qt_gui': % elif generate_options == 'qt_gui':
${class_name}::${class_name} () : QWidget(), top_block("display_qt") { ${class_name}::${class_name} (${param_str}) : QWidget() {
this->setWindowTitle("${title}"); this->setWindowTitle("${title}");
// check_set_qss // check_set_qss
// set icon // set icon
@ -58,11 +62,15 @@ ${class_name}::${class_name} () : QWidget(), top_block("display_qt") {
## self._lock = threading.RLock() ## self._lock = threading.RLock()
% endif % endif
% if not generate_options.startswith('hb'):
this->tb = make_top_block("${title}");
% endif
% if blocks: % if blocks:
// Blocks: // Blocks:
% for blk, blk_make, declarations in blocks: % for blk, blk_make, declarations in blocks:
{ {
${indent(blk_make)} ${doubleindent(blk_make)}
## % if 'alias' in blk.params and blk.params['alias'].get_evaluated(): ## % if 'alias' in blk.params and blk.params['alias'].get_evaluated():
## ${blk.name}.set_block_alias("${blk.params['alias'].get_evaluated()}") ## ${blk.name}.set_block_alias("${blk.params['alias'].get_evaluated()}")
## % endif ## % endif
@ -107,11 +115,31 @@ void ${class_name}::set_${var.name} (${var.vtype} ${var.name}) {
% endfor % endfor
% endif % endif
} }
% endfor % endfor
int main (int argc, char **argv) { int main (int argc, char **argv) {
% if parameters: % if parameters:
## parse args % for parameter in parameters:
${parameter.vtype} ${parameter.name};
% endfor
po::options_description desc("Options");
desc.add_options()
("help", "display help")
% for parameter in parameters:
("${parameter.name}", po::value<${parameter.vtype}>(&${parameter.name}), "${parameter.label}")
% endfor
;
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);
if (vm.count("help")) {
std::cout << desc << std::endl;
return 0;
}
% endif % endif
% if flow_graph.get_option('realtime_scheduling'): % if flow_graph.get_option('realtime_scheduling'):
if (enable_realtime_scheduling() != RT_OK) { if (enable_realtime_scheduling() != RT_OK) {
@ -123,7 +151,7 @@ int main (int argc, char **argv) {
${class_name}* top_block = new ${class_name}(); ${class_name}* top_block = new ${class_name}();
## TODO: params ## TODO: params
% if flow_graph.get_option('run_options') == 'prompt': % if flow_graph.get_option('run_options') == 'prompt':
top_block->start(); top_block->tb->start();
% for m in monitors: % for m in monitors:
(top_block->${m.name}).start(); (top_block->${m.name}).start();
% endfor % endfor
@ -131,7 +159,7 @@ int main (int argc, char **argv) {
std::cin.ignore(); std::cin.ignore();
top_block->stop(); top_block->stop();
% elif flow_graph.get_option('run_options') == 'run': % elif flow_graph.get_option('run_options') == 'run':
top_block->start(); top_block->tb->start();
% endif % endif
% for m in monitors: % for m in monitors:
(top_block->${m.name}).start(); (top_block->${m.name}).start();
@ -140,9 +168,9 @@ int main (int argc, char **argv) {
% elif generate_options == 'qt_gui': % elif generate_options == 'qt_gui':
QApplication app(argc, argv); QApplication app(argc, argv);
${class_name} *top_block = new ${class_name}(); ${class_name} *top_block = new ${class_name}(${param_str_without_types});
top_block->start(); top_block->tb->start();
top_block->show(); top_block->show();
app.exec(); app.exec();

View File

@ -1,3 +1,4 @@
<%def name="indent(code)">${ ' ' + '\n '.join(str(code).splitlines()) }</%def>\
#ifndef ${flow_graph.get_option('id').upper()}_HPP #ifndef ${flow_graph.get_option('id').upper()}_HPP
#define ${flow_graph.get_option('id').upper()}_HPP #define ${flow_graph.get_option('id').upper()}_HPP
/******************** /********************
@ -28,19 +29,25 @@ ${inc}
#include <QSettings> #include <QSettings>
% endif % endif
% if parameters:
#include <iostream>
#include <boost/program_options.hpp>
% endif
using namespace gr; using namespace gr;
<% <%
class_name = flow_graph.get_option('id') class_name = flow_graph.get_option('id')
## TODO: param_str param_str = ", ".join((param.vtype + " " + param.name) for param in parameters)
%>\ %>\
% if generate_options == 'no_gui': % if generate_options == 'no_gui':
class ${class_name} : public top_block { class ${class_name} {
% elif generate_options.startswith('hb'): % elif generate_options.startswith('hb'):
class ${class_name} : public hier_block2 { class ${class_name} : public hier_block2 {
% elif generate_options == 'qt_gui': % elif generate_options == 'qt_gui':
class ${class_name} : public QWidget, public top_block { class ${class_name} : public QWidget {
Q_OBJECT Q_OBJECT
% endif % endif
@ -56,15 +63,15 @@ private:
% for block, make, declarations in blocks: % for block, make, declarations in blocks:
% if declarations: % if declarations:
${declarations} ${indent(declarations)}
% endif % endif
% endfor % endfor
% if parameters: % if parameters:
// Parameters: // Parameters:
% for param in parameters: % for param in parameters:
${param.get_var_make()} ${param.get_cpp_var_make()}
% endfor % endfor
% endif % endif
@ -76,8 +83,10 @@ private:
% endif % endif
public: public:
${class_name}(); % if not generate_options.startswith('hb'):
## TODO: param_str top_block_sptr tb;
% endif
${class_name}(${param_str});
~${class_name}(); ~${class_name}();
% for var in parameters + variables: % for var in parameters + variables:

View File

@ -60,6 +60,7 @@ class CppTopBlockGenerator(TopBlockGenerator):
parameters = fg.get_parameters() parameters = fg.get_parameters()
monitors = fg.get_monitors() monitors = fg.get_monitors()
self._variable_types() self._variable_types()
self._parameter_types()
self.namespace = { self.namespace = {
'flow_graph': fg, 'flow_graph': fg,
@ -245,6 +246,14 @@ class CppTopBlockGenerator(TopBlockGenerator):
for var in variables: for var in variables:
var.decide_type() var.decide_type()
def _parameter_types(self):
fg = self._flow_graph
parameters = fg.get_parameters()
for param in parameters:
type_translation = {'eng_float' : 'double', 'intx' : 'int', 'std' : 'std::string'};
param.vtype = type_translation[param.params['type'].value]
def _callbacks(self): def _callbacks(self):
fg = self._flow_graph fg = self._flow_graph
variables = fg.get_cpp_variables() variables = fg.get_cpp_variables()
@ -338,7 +347,7 @@ class CppTopBlockGenerator(TopBlockGenerator):
for con in sorted(connections, key=by_domain_and_blocks): for con in sorted(connections, key=by_domain_and_blocks):
template = templates[con.type] template = templates[con.type]
code = template.render(make_port_sig=make_port_sig, source=con.source_port, sink=con.sink_port) code = template.render(make_port_sig=make_port_sig, source=con.source_port, sink=con.sink_port)
code = 'this->' + code code = 'this->tb->' + code
rendered.append(code) rendered.append(code)
return rendered return rendered

View File

@ -266,6 +266,9 @@ class TopBlockGenerator(object):
# Remove the virtual connection # Remove the virtual connection
connections.remove(connection) connections.remove(connection)
for connection in virtual_sink_connections:
connections.remove(connection)
# Bypassing blocks: Need to find all the enabled connections for the block using # Bypassing blocks: Need to find all the enabled connections for the block using
# the *connections* object rather than get_connections(). Create new connections # the *connections* object rather than get_connections(). Create new connections
# that bypass the selected block and remove the existing ones. This allows adjacent # that bypass the selected block and remove the existing ones. This allows adjacent

View File

@ -39,6 +39,7 @@ CPP_TEMPLATES_SCHEME = expand(
includes=list, includes=list,
declarations=str_, declarations=str_,
make=str_, make=str_,
var_make=str_,
callbacks=list, callbacks=list,
link=list, link=list,
translations=dict, translations=dict,

View File

@ -3,6 +3,7 @@ from .utils import Spec, expand, str_
DOMAIN_CONNECTION = expand( DOMAIN_CONNECTION = expand(
type=Spec(types=list, required=True, item_scheme=None), type=Spec(types=list, required=True, item_scheme=None),
connect=str_, connect=str_,
cpp_connect=str_,
) )
DOMAIN_SCHEME = expand( DOMAIN_SCHEME = expand(

View File

@ -79,49 +79,6 @@ class ExecFlowGraphThread(threading.Thread):
shell=False, universal_newlines=True shell=False, universal_newlines=True
) )
def _cpp_run_cmake(self):
"""
Generate and compile this C++ flow graph.
"""
generator = self.page.get_generator()
xterm_executable = find_executable(self.xterm_executable)
dirname = generator.file_path
builddir = os.path.join(dirname, 'build')
run_command_args = [ 'cmake', '..' ]
Messages.send_start_exec(' '.join(run_command_args))
return subprocess.Popen(
args=run_command_args,
cwd=builddir,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
shell=False, universal_newlines=True
)
def _cpp_compile(self):
"""
Compile this C++ flow graph.
"""
generator = self.page.get_generator()
xterm_executable = find_executable(self.xterm_executable)
dirname = generator.file_path
builddir = os.path.join(dirname, 'build')
run_command_args = [ 'make' ]
Messages.send_start_exec(' '.join(run_command_args))
return subprocess.Popen(
args=run_command_args,
cwd=builddir,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
shell=False, universal_newlines=True
)
def _cpp_popen(self): def _cpp_popen(self):
""" """
Execute this C++ flow graph after generating and compiling it. Execute this C++ flow graph after generating and compiling it.
@ -129,22 +86,22 @@ class ExecFlowGraphThread(threading.Thread):
generator = self.page.get_generator() generator = self.page.get_generator()
run_command = generator.file_path + '/build/' + self.flow_graph.get_option('id') run_command = generator.file_path + '/build/' + self.flow_graph.get_option('id')
dirname = generator.file_path
builddir = os.path.join(dirname, 'build')
if os.path.isfile(run_command): if os.path.isfile(run_command):
os.remove(run_command) os.remove(run_command)
xterm_executable = find_executable(self.xterm_executable) xterm_executable = find_executable(self.xterm_executable)
process = self._cpp_run_cmake()
process.wait()
process = self._cpp_compile()
process.wait()
run_command_args = [xterm_executable, '-e', run_command] run_command_args = ['cmake .. &&', 'make && ', xterm_executable, '-e', run_command]
Messages.send_start_exec(' '.join(run_command_args)) Messages.send_start_exec(' '.join(run_command_args))
return subprocess.Popen( return subprocess.Popen(
args=run_command_args, args=' '.join(run_command_args),
cwd=builddir,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
shell=False, universal_newlines=True shell=True, universal_newlines=True
) )
def run(self): def run(self):