Skip to content

Commit

Permalink
GRC YAML: Support vectors, format bool as true|false
Browse files Browse the repository at this point in the history
  • Loading branch information
frankosterfeld committed Oct 24, 2023
1 parent c6afa5c commit 187e473
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 76 deletions.
87 changes: 54 additions & 33 deletions core/include/gnuradio-4.0/Graph_yaml_importer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,36 @@
namespace gr {

namespace detail {

template<typename T>
inline auto
toYamlString(const T &value) {
if constexpr (std::is_same_v<std::string, std::remove_cvref_t<T>>) {
return value;
} else if constexpr (std::is_same_v<bool, std::remove_cvref_t<T>>) {
return value ? "true" : "false";
} else if constexpr (requires { std::to_string(value); }) {
return std::to_string(value);
} else {
return "";
}
}

struct YamlSeq {
YAML::Emitter &out;

YamlSeq(YAML::Emitter &out_) : out(out_) { out << YAML::BeginSeq; }

~YamlSeq() { out << YAML::EndSeq; }

template<typename F>
requires std::is_invocable_v<F>
void
write_fn(const char * /*key*/, F &&fun) {
fun();
}
};

struct YamlMap {
YAML::Emitter &out;

Expand All @@ -24,31 +54,24 @@ struct YamlMap {

template<typename T>
void
write(const std::string_view &key, const T &value) {
write(const std::string_view &key, const std::vector<T> &value) {
out << YAML::Key << key.data();
out << YAML::Value << value;
YamlSeq seq(out);
for (const auto &elem : value) out << YAML::Value << toYamlString(elem);
}

template<typename F>
template<typename T>
void
write_fn(const std::string_view &key, F &&fun) {
write(const std::string_view &key, const T &value) {
out << YAML::Key << key.data();
out << YAML::Value;
fun();
out << YAML::Value << toYamlString(value);
}
};

struct YamlSeq {
YAML::Emitter &out;

YamlSeq(YAML::Emitter &out_) : out(out_) { out << YAML::BeginSeq; }

~YamlSeq() { out << YAML::EndSeq; }

template<typename F>
requires std::is_invocable_v<F>
void
write_fn(const char * /*key*/, F &&fun) {
write_fn(const std::string_view &key, F &&fun) {
out << YAML::Key << key.data();
out << YAML::Value;
fun();
}
};
Expand Down Expand Up @@ -98,14 +121,19 @@ load_grc(plugin_loader &loader, const std::string &yaml_source) {

// This is a known property of this node
auto try_type = [&]<typename T>() {
if (it->second.index() != variant_type_list::index_of<T>()) {
return false;
if (it->second.index() == variant_type_list::index_of<T>()) {
const auto &value = grc_value.template as<T>();
new_properties[key] = value;
return true;
}

const auto &value = grc_value.template as<T>();
new_properties[key] = value;
if (it->second.index() == variant_type_list::index_of<std::vector<T>>()) {
const auto &value = grc_value.template as<std::vector<T>>();
new_properties[key] = value;
return true;
}

return true;
return false;
};

// clang-format off
Expand All @@ -117,6 +145,9 @@ load_grc(plugin_loader &loader, const std::string &yaml_source) {
try_type.operator()<std::uint16_t>() ||
try_type.operator()<std::uint32_t>() ||
try_type.operator()<std::uint64_t>() ||
try_type.operator()<bool>() ||
try_type.operator()<float>() ||
try_type.operator()<double>() ||
try_type.operator()<std::string>() ||
[&] {
// Fallback to string, and non-defined property
Expand Down Expand Up @@ -203,18 +234,8 @@ save_grc(const gr::Graph &testGraph) {
map.write_fn("parameters", [&]() {
detail::YamlMap parameters(out);
auto write_map = [&](const auto &local_map) {
for (const auto &settings_pair : local_map) {
std::visit(
[&]<typename T>(const T &value) {
if constexpr (std::is_same_v<std::string, std::remove_cvref_t<T>>) {
parameters.write(settings_pair.first, value);
} else if constexpr (requires { std::to_string(value); }) {
parameters.write(settings_pair.first, std::to_string(value));
} else {
// not supported
}
},
settings_pair.second);
for (const auto &[settingsKey, settingsValue] : local_map) {
std::visit([&]<typename T>(const T &value) { parameters.write(settingsKey, value); }, settingsValue);
}
};

Expand Down
91 changes: 68 additions & 23 deletions core/test/app_grc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,17 @@ ENABLE_REFLECTION_FOR_TEMPLATE(ArraySource, outA, outB);

template<typename T>
struct ArraySink : public gr::Block<ArraySink<T>> {
std::array<gr::PortIn<T>, 2> inA;
std::array<gr::PortIn<T>, 2> inB;
std::array<gr::PortIn<T>, 2> inA;
std::array<gr::PortIn<T>, 2> inB;
gr::Annotated<bool, "bool setting"> bool_setting = false;
gr::Annotated<std::string, "String setting"> string_setting;
gr::Annotated<std::vector<bool>, "Bool vector setting"> bool_vector;
gr::Annotated<std::vector<std::string>, "String vector setting"> string_vector;
gr::Annotated<std::vector<double>, "Double vector setting"> double_vector;
gr::Annotated<std::vector<int16_t>, "int16_t vector setting"> int16_vector;
};

ENABLE_REFLECTION_FOR_TEMPLATE(ArraySink, inA, inB);
ENABLE_REFLECTION_FOR_TEMPLATE(ArraySink, inA, inB, bool_setting, string_setting, bool_vector, string_vector, double_vector, int16_vector);

struct TestContext {
TestContext(std::vector<std::filesystem::path> paths) : registry(), loader(&registry, std::move(paths)) {}
Expand All @@ -35,20 +41,23 @@ struct TestContext {
};

namespace {
auto collectBlocks(const gr::Graph &graph) {
std::set<std::string> result;
graph.forEachBlock([&](const auto &node) { result.insert(fmt::format("{}-{}", node.name(), node.typeName())); });
return result;
};
auto
collectBlocks(const gr::Graph &graph) {
std::set<std::string> result;
graph.forEachBlock([&](const auto &node) { result.insert(fmt::format("{}-{}", node.name(), node.typeName())); });
return result;
};

auto collectEdges(const gr::Graph &graph) {
std::set<std::string> result;
graph.forEachEdge([&](const auto &edge) {
result.insert(fmt::format("{}#{}#{} - {}#{}#{}", edge.sourceBlock().name(), edge.sourcePortDefinition().topLevel, edge.sourcePortDefinition().subIndex, edge.destinationBlock().name(), edge.destinationPortDefinition().topLevel, edge.destinationPortDefinition().subIndex));
});
return result;
};
}
auto
collectEdges(const gr::Graph &graph) {
std::set<std::string> result;
graph.forEachEdge([&](const auto &edge) {
result.insert(fmt::format("{}#{}#{} - {}#{}#{}", edge.sourceBlock().name(), edge.sourcePortDefinition().topLevel, edge.sourcePortDefinition().subIndex, edge.destinationBlock().name(),
edge.destinationPortDefinition().topLevel, edge.destinationPortDefinition().subIndex));
});
return result;
};
} // namespace

int
main(int argc, char *argv[]) {
Expand Down Expand Up @@ -94,12 +103,12 @@ main(int argc, char *argv[]) {
using namespace gr;
registerBuiltinBlocks(&context.registry);

auto graph_source = read_file(TESTS_SOURCE_PATH "/grc/test.grc");
auto graph_source = read_file(TESTS_SOURCE_PATH "/grc/test.grc");

auto graph_1 = gr::load_grc(context.loader, graph_source);
auto graph_saved_source = gr::save_grc(graph_1);
auto graph_1 = gr::load_grc(context.loader, graph_source);
auto graph_saved_source = gr::save_grc(graph_1);

auto graph_2 = gr::load_grc(context.loader, graph_saved_source);
auto graph_2 = gr::load_grc(context.loader, graph_saved_source);

assert(collectBlocks(graph_1) == collectBlocks(graph_2));
assert(collectEdges(graph_1) == collectEdges(graph_2));
Expand All @@ -113,9 +122,9 @@ main(int argc, char *argv[]) {
GP_REGISTER_NODE_RUNTIME(&context.registry, ArraySink, double);

gr::Graph graph_1;
auto &arraySink = graph_1.emplaceBlock<ArraySink<double>>();
auto &arraySource0 = graph_1.emplaceBlock<ArraySource<double>>();
auto &arraySource1 = graph_1.emplaceBlock<ArraySource<double>>();
auto &arraySink = graph_1.emplaceBlock<ArraySink<double>>();
auto &arraySource0 = graph_1.emplaceBlock<ArraySource<double>>();
auto &arraySource1 = graph_1.emplaceBlock<ArraySource<double>>();

graph_1.connect<"outA", 0>(arraySource0).to<"inB", 1>(arraySink);
graph_1.connect<"outA", 1>(arraySource1).to<"inB", 0>(arraySink);
Expand All @@ -130,4 +139,40 @@ main(int argc, char *argv[]) {
assert(collectBlocks(graph_1) == collectBlocks(graph_2));
assert(collectEdges(graph_1) == collectEdges(graph_2));
}

// Test settings serialization
{
using namespace gr;
registerBuiltinBlocks(&context.registry);
GP_REGISTER_NODE_RUNTIME(&context.registry, ArraySink, double);

gr::Graph graph_1;
const auto expectedString = std::string("abc");
const auto expectedBool = true;
const auto expectedStringVector = std::vector<std::string>{ "a", "b", "c" };
const auto expectedBoolVector = std::vector<bool>{ true, false, true };
const auto expectedDoubleVector = std::vector<double>{ 1., 2., 3. };
const auto expectedInt16Vector = std::vector<int16_t>{ 1, 2, 3 };
auto &arraySink = graph_1.emplaceBlock<ArraySink<double>>({ { "bool_setting", expectedBool },
{ "string_setting", expectedString },
{ "bool_vector", expectedBoolVector },
{ "string_vector", expectedStringVector },
{ "double_vector", expectedDoubleVector },
{ "int16_vector", expectedInt16Vector } });

const auto graph_1_saved = gr::save_grc(graph_1);
const auto graph_2 = gr::load_grc(context.loader, graph_1_saved);
graph_2.forEachBlock([&](const auto &node) {
const auto settings = node.settings().get();
assert(std::get<bool>(settings.at("bool_setting")) == expectedBool);
assert(std::get<std::string>(settings.at("string_setting")) == expectedString);
assert(std::get<std::vector<bool>>(settings.at("bool_vector")) == expectedBoolVector);
assert(std::get<std::vector<std::string>>(settings.at("string_vector")) == expectedStringVector);
assert(std::get<std::vector<double>>(settings.at("double_vector")) == expectedDoubleVector);
assert(std::get<std::vector<int16_t>>(settings.at("int16_vector")) == expectedInt16Vector);
});

assert(collectBlocks(graph_1) == collectBlocks(graph_2));
assert(collectEdges(graph_1) == collectEdges(graph_2));
}
}
40 changes: 20 additions & 20 deletions core/test/grc/test.grc.expected
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,24 @@ blocks:
denominator::description: denominator
denominator::documentation: "Bottom of resampling ratio (<1: Decimate, >1: Interpolate, =1: No change)"
denominator::unit: ""
denominator::visible: 0
denominator::visible: false
description: ""
meta_information::description: meta-information
meta_information::documentation: store non-graph-processing information like UI block position etc.
meta_information::unit: ""
meta_information::visible: 0
meta_information::visible: false
name::description: user-defined name
name::documentation: N.B. may not be unique -> ::unique_name
name::unit: ""
name::visible: 0
name::visible: false
numerator::description: numerator
numerator::documentation: "Top of resampling ratio (<1: Decimate, >1: Interpolate, =1: No change)"
numerator::unit: ""
numerator::visible: 0
numerator::visible: false
stride::description: stride
stride::documentation: samples between data processing. <N for overlap, >N for skip, =0 for back-to-back.
stride::unit: ""
stride::visible: 0
stride::visible: false
unknown_property: 42
- name: multiplier
id: good::multiply
Expand All @@ -41,24 +41,24 @@ blocks:
denominator::description: denominator
denominator::documentation: "Bottom of resampling ratio (<1: Decimate, >1: Interpolate, =1: No change)"
denominator::unit: ""
denominator::visible: 0
denominator::visible: false
description: ""
meta_information::description: meta-information
meta_information::documentation: store non-graph-processing information like UI block position etc.
meta_information::unit: ""
meta_information::visible: 0
meta_information::visible: false
name::description: user-defined name
name::documentation: N.B. may not be unique -> ::unique_name
name::unit: ""
name::visible: 0
name::visible: false
numerator::description: numerator
numerator::documentation: "Top of resampling ratio (<1: Decimate, >1: Interpolate, =1: No change)"
numerator::unit: ""
numerator::visible: 0
numerator::visible: false
stride::description: stride
stride::documentation: samples between data processing. <N for overlap, >N for skip, =0 for back-to-back.
stride::unit: ""
stride::visible: 0
stride::visible: false
- name: counter
id: builtin_counter
parameters:
Expand All @@ -70,24 +70,24 @@ blocks:
denominator::description: denominator
denominator::documentation: "Bottom of resampling ratio (<1: Decimate, >1: Interpolate, =1: No change)"
denominator::unit: ""
denominator::visible: 0
denominator::visible: false
description: ""
meta_information::description: meta-information
meta_information::documentation: store non-graph-processing information like UI block position etc.
meta_information::unit: ""
meta_information::visible: 0
meta_information::visible: false
name::description: user-defined name
name::documentation: N.B. may not be unique -> ::unique_name
name::unit: ""
name::visible: 0
name::visible: false
numerator::description: numerator
numerator::documentation: "Top of resampling ratio (<1: Decimate, >1: Interpolate, =1: No change)"
numerator::unit: ""
numerator::visible: 0
numerator::visible: false
stride::description: stride
stride::documentation: samples between data processing. <N for overlap, >N for skip, =0 for back-to-back.
stride::unit: ""
stride::visible: 0
stride::visible: false
- name: sink
id: good::cout_sink
parameters:
Expand All @@ -100,24 +100,24 @@ blocks:
denominator::description: denominator
denominator::documentation: "Bottom of resampling ratio (<1: Decimate, >1: Interpolate, =1: No change)"
denominator::unit: ""
denominator::visible: 0
denominator::visible: false
description: ""
meta_information::description: meta-information
meta_information::documentation: store non-graph-processing information like UI block position etc.
meta_information::unit: ""
meta_information::visible: 0
meta_information::visible: false
name::description: user-defined name
name::documentation: N.B. may not be unique -> ::unique_name
name::unit: ""
name::visible: 0
name::visible: false
numerator::description: numerator
numerator::documentation: "Top of resampling ratio (<1: Decimate, >1: Interpolate, =1: No change)"
numerator::unit: ""
numerator::visible: 0
numerator::visible: false
stride::description: stride
stride::documentation: samples between data processing. <N for overlap, >N for skip, =0 for back-to-back.
stride::unit: ""
stride::visible: 0
stride::visible: false
unknown_property: 42
connections:
- [main_source, 0, multiplier, 0]
Expand Down

0 comments on commit 187e473

Please sign in to comment.