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_
label: Import
flags: python
parameters:
- id: imports

View File

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

View File

@ -1,6 +1,6 @@
id: options
label: Options
flags: has_python, has_cpp
flags: python, cpp
parameters:
- id: title
@ -156,7 +156,7 @@ templates:
else: self.stop(); self.wait()'
cpp_templates:
includes: '#include <gnuradio/top_block.h>'
includes: ['#include <gnuradio/top_block.h>']
documentation: |-
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
label: Pad Sink
flags: python
parameters:
- id: label

View File

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

View File

@ -1,5 +1,6 @@
id: parameter
label: Parameter
flags: python, cpp
parameters:
- id: label
@ -37,6 +38,10 @@ templates:
var_make: self.${id} = ${id}
make: ${value}
cpp_templates:
var_make: ${type.type} ${id} = ${id};
make: ${value}
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.

View File

@ -7,5 +7,5 @@ multiple_connections_per_output: true
templates:
- 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) })

View File

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

View File

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

View File

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

View File

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

View File

@ -169,9 +169,14 @@ class Block(Element):
"""check if this block supports the selected 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.")
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):
"""or variables check the value (only if var_value is used)"""
if self.is_variable and self.value != 'value':
@ -266,6 +271,8 @@ class Block(Element):
a list of strings
"""
def make_callback(callback):
if self.is_variable:
return callback
if 'this->' in callback:
return 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_minimum_required(VERSION 3.8) Which version?
cmake_minimum_required(VERSION 3.8) # Which version?
% if generate_options == 'qt_gui':
find_package(Qt5Widgets REQUIRED)
@ -50,11 +50,14 @@ set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
set(GR_LIBRARIES
boost_system
% if parameters:
boost_program_options
% endif
gnuradio-blocks
gnuradio-runtime
gnuradio-pmt
% for link in links:
% if link != '':
% if link:
${link}
% endif
% endfor

View File

@ -1,3 +1,4 @@
<%def name="doubleindent(code)">${ '\n '.join(str(code).splitlines()) }</%def>\
/********************
GNU Radio C++ Flow Graph Source File
@ -12,21 +13,24 @@ Generated: ${generated_time}
********************/
#include "${flow_graph.get_option('id')}.hpp"
% if parameters:
namespace po = boost::program_options;
% endif
using namespace gr;
<%
class_name = flow_graph.get_option('id')
## TODO: param_str
class_name = flow_graph.get_option('id')
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':
${class_name}::${class_name} () : top_block("${title}") {
${class_name}::${class_name} (${param_str}) {
% elif generate_options.startswith('hb'):
## 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'):
message_port_register_hier_in("${pad['label']}")
% endfor
@ -34,7 +38,7 @@ ${class_name}::${class_name} () : hier_block2("${title}") {
message_port_register_hier_out("${pad['label']}")
% endfor
% elif generate_options == 'qt_gui':
${class_name}::${class_name} () : QWidget(), top_block("display_qt") {
${class_name}::${class_name} (${param_str}) : QWidget() {
this->setWindowTitle("${title}");
// check_set_qss
// set icon
@ -58,11 +62,15 @@ ${class_name}::${class_name} () : QWidget(), top_block("display_qt") {
## self._lock = threading.RLock()
% endif
% if not generate_options.startswith('hb'):
this->tb = make_top_block("${title}");
% endif
% if blocks:
// 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():
## ${blk.name}.set_block_alias("${blk.params['alias'].get_evaluated()}")
## % endif
@ -107,11 +115,31 @@ void ${class_name}::set_${var.name} (${var.vtype} ${var.name}) {
% endfor
% endif
}
% endfor
int main (int argc, char **argv) {
% 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
% if flow_graph.get_option('realtime_scheduling'):
if (enable_realtime_scheduling() != RT_OK) {
@ -123,7 +151,7 @@ int main (int argc, char **argv) {
${class_name}* top_block = new ${class_name}();
## TODO: params
% if flow_graph.get_option('run_options') == 'prompt':
top_block->start();
top_block->tb->start();
% for m in monitors:
(top_block->${m.name}).start();
% endfor
@ -131,7 +159,7 @@ int main (int argc, char **argv) {
std::cin.ignore();
top_block->stop();
% elif flow_graph.get_option('run_options') == 'run':
top_block->start();
top_block->tb->start();
% endif
% for m in monitors:
(top_block->${m.name}).start();
@ -140,9 +168,9 @@ int main (int argc, char **argv) {
% elif generate_options == 'qt_gui':
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();
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
#define ${flow_graph.get_option('id').upper()}_HPP
/********************
@ -28,19 +29,25 @@ ${inc}
#include <QSettings>
% endif
% if parameters:
#include <iostream>
#include <boost/program_options.hpp>
% endif
using namespace gr;
<%
class_name = flow_graph.get_option('id')
## TODO: param_str
class_name = flow_graph.get_option('id')
param_str = ", ".join((param.vtype + " " + param.name) for param in parameters)
%>\
% if generate_options == 'no_gui':
class ${class_name} : public top_block {
class ${class_name} {
% elif generate_options.startswith('hb'):
class ${class_name} : public hier_block2 {
% elif generate_options == 'qt_gui':
class ${class_name} : public QWidget, public top_block {
class ${class_name} : public QWidget {
Q_OBJECT
% endif
@ -56,15 +63,15 @@ private:
% for block, make, declarations in blocks:
% if declarations:
${declarations}
% endif
% if declarations:
${indent(declarations)}
% endif
% endfor
% if parameters:
// Parameters:
% for param in parameters:
${param.get_var_make()}
${param.get_cpp_var_make()}
% endfor
% endif
@ -76,8 +83,10 @@ private:
% endif
public:
${class_name}();
## TODO: param_str
% if not generate_options.startswith('hb'):
top_block_sptr tb;
% endif
${class_name}(${param_str});
~${class_name}();
% for var in parameters + variables:

View File

@ -60,6 +60,7 @@ class CppTopBlockGenerator(TopBlockGenerator):
parameters = fg.get_parameters()
monitors = fg.get_monitors()
self._variable_types()
self._parameter_types()
self.namespace = {
'flow_graph': fg,
@ -245,6 +246,14 @@ class CppTopBlockGenerator(TopBlockGenerator):
for var in variables:
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):
fg = self._flow_graph
variables = fg.get_cpp_variables()
@ -338,7 +347,7 @@ class CppTopBlockGenerator(TopBlockGenerator):
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)
code = 'this->' + code
code = 'this->tb->' + code
rendered.append(code)
return rendered

View File

@ -266,6 +266,9 @@ class TopBlockGenerator(object):
# Remove the virtual 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
# the *connections* object rather than get_connections(). Create new connections
# 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,
declarations=str_,
make=str_,
var_make=str_,
callbacks=list,
link=list,
translations=dict,

View File

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

View File

@ -79,49 +79,6 @@ class ExecFlowGraphThread(threading.Thread):
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):
"""
Execute this C++ flow graph after generating and compiling it.
@ -129,22 +86,22 @@ class ExecFlowGraphThread(threading.Thread):
generator = self.page.get_generator()
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):
os.remove(run_command)
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))
return subprocess.Popen(
args=run_command_args,
args=' '.join(run_command_args),
cwd=builddir,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
shell=False, universal_newlines=True
shell=True, universal_newlines=True
)
def run(self):