Skip to content

Commit

Permalink
Improve support for sub-graphs
Browse files Browse the repository at this point in the history
- Allow sub-graphs to be added to gr::Graph
- Allow exporting ports from graphs
- Allow reading and writing sub-graphs into GRC

Signed-off-by: Ivan Čukić <[email protected]>
  • Loading branch information
ivan-cukic committed Dec 8, 2024
1 parent 2e79797 commit c002328
Show file tree
Hide file tree
Showing 13 changed files with 768 additions and 455 deletions.
8 changes: 7 additions & 1 deletion blocks/soapy/src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
add_executable(soapy_example soapy_example.cpp)
target_link_libraries(soapy_example PRIVATE gr-basic gr-fileio gr-testing gr-soapy ut)
target_link_libraries(
soapy_example
PRIVATE gr-basic
gr-fileio
gr-testing
gr-soapy
ut)
10 changes: 9 additions & 1 deletion blocks/soapy/test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
add_executable(qa_Soapy qa_Soapy.cpp)
target_include_directories(qa_Soapy PRIVATE ${CMAKE_BINARY_DIR}/include ${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(qa_Soapy PRIVATE gnuradio-options gnuradio-core fmt fftw gr-soapy gr-testing ut)
target_link_libraries(
qa_Soapy
PRIVATE gnuradio-options
gnuradio-core
fmt
fftw
gr-soapy
gr-testing
ut)
add_test(NAME qa_Soapy COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} ${CMAKE_CURRENT_BINARY_DIR}/qa_Soapy)
43 changes: 25 additions & 18 deletions core/include/gnuradio-4.0/Block.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ enum class Category {
* }
*
* void start() override {
* propertyCallbacks.emplace(kMyCustomProperty, &MyBlock::propertyCallbackMyCustom);
* propertyCallbacks.emplace(kMyCustomProperty, std::mem_fn(&MyBlock::propertyCallbackMyCustom));
* }
* };
* @endcode
Expand Down Expand Up @@ -428,18 +428,18 @@ class Block : public lifecycle::StateMachine<Derived> {
MsgPortInBuiltin msgIn;
MsgPortOutBuiltin msgOut;

using PropertyCallback = std::optional<Message> (Derived::*)(std::string_view, Message);
using PropertyCallback = std::function<std::optional<Message>(Derived&, std::string_view, Message)>;
std::map<std::string, PropertyCallback> propertyCallbacks{
{block::property::kHeartbeat, &Block::propertyCallbackHeartbeat}, //
{block::property::kEcho, &Block::propertyCallbackEcho}, //
{block::property::kLifeCycleState, &Block::propertyCallbackLifecycleState}, //
{block::property::kSetting, &Block::propertyCallbackSettings}, //
{block::property::kStagedSetting, &Block::propertyCallbackStagedSettings}, //
{block::property::kStoreDefaults, &Block::propertyCallbackStoreDefaults}, //
{block::property::kResetDefaults, &Block::propertyCallbackResetDefaults}, //
{block::property::kActiveContext, &Block::propertyCallbackActiveContext}, //
{block::property::kSettingsCtx, &Block::propertyCallbackSettingsCtx}, //
{block::property::kSettingsContexts, &Block::propertyCallbackSettingsContexts}, //
{block::property::kHeartbeat, std::mem_fn(&Block::propertyCallbackHeartbeat)}, //
{block::property::kEcho, std::mem_fn(&Block::propertyCallbackEcho)}, //
{block::property::kLifeCycleState, std::mem_fn(&Block::propertyCallbackLifecycleState)}, //
{block::property::kSetting, std::mem_fn(&Block::propertyCallbackSettings)}, //
{block::property::kStagedSetting, std::mem_fn(&Block::propertyCallbackStagedSettings)}, //
{block::property::kStoreDefaults, std::mem_fn(&Block::propertyCallbackStoreDefaults)}, //
{block::property::kResetDefaults, std::mem_fn(&Block::propertyCallbackResetDefaults)}, //
{block::property::kActiveContext, std::mem_fn(&Block::propertyCallbackActiveContext)}, //
{block::property::kSettingsCtx, std::mem_fn(&Block::propertyCallbackSettingsCtx)}, //
{block::property::kSettingsContexts, std::mem_fn(&Block::propertyCallbackSettingsContexts)}, //
};
std::map<std::string, std::set<std::string>> propertySubscriptions;

Expand Down Expand Up @@ -1358,17 +1358,20 @@ class Block : public lifecycle::StateMachine<Derived> {
}

std::size_t getMergedBlockLimit() {
if constexpr (requires(const Derived& d) {
{ available_samples(d) } -> std::same_as<std::size_t>;
}) {
if constexpr (Derived::blockCategory != block::Category::NormalBlock) {
return 0;
} else if constexpr (requires(const Derived& d) {
{ available_samples(d) } -> std::same_as<std::size_t>;
}) {
return available_samples(self());
} else if constexpr (traits::block::stream_input_port_types<Derived>::size == 0 && traits::block::stream_output_port_types<Derived>::size == 0) { // allow blocks that have neither input nor output ports (by merging source to sink block) -> use internal buffer size
constexpr gr::Size_t chunkSize = Derived::merged_work_chunk_size();
static_assert(chunkSize != std::dynamic_extent && chunkSize > 0, "At least one internal port must define a maximum number of samples or the non-member/hidden "
"friend function `available_samples(const BlockType&)` must be defined.");
return chunkSize;
} else {
return std::numeric_limits<std::size_t>::max();
}
return std::numeric_limits<std::size_t>::max();
}

template<typename TIn, typename TOut>
Expand Down Expand Up @@ -1670,7 +1673,11 @@ class Block : public lifecycle::StateMachine<Derived> {
}
}
} else { // block does not define any valid processing function
static_assert(meta::always_false<traits::block::stream_input_port_types_tuple<Derived>>, "neither processBulk(...) nor processOne(...) implemented");
if constexpr (Derived::blockCategory != block::Category::NormalBlock) {
return {requestedWork, 0UZ, OK};
} else {
static_assert(meta::always_false<traits::block::stream_input_port_types_tuple<Derived>>, "neither processBulk(...) nor processOne(...) implemented");
}
}

// sanitise input/output samples based on explicit user-defined processBulk(...) return status
Expand Down Expand Up @@ -1852,7 +1859,7 @@ class Block : public lifecycle::StateMachine<Derived> {

std::optional<Message> retMessage;
try {
retMessage = (self().*callback)(message.endpoint, message); // N.B. life-time: message is copied
retMessage = callback(self(), message.endpoint, message); // N.B. life-time: message is copied
} catch (const gr::exception& e) {
retMessage = Message{message};
retMessage->data = std::unexpected(Error(e));
Expand Down
80 changes: 49 additions & 31 deletions core/include/gnuradio-4.0/BlockModel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,26 @@ struct Edge {

Edge& operator=(const Edge&) = delete;

Edge(Edge&&) noexcept = default;

Edge& operator=(Edge&&) noexcept = default;
Edge(Edge&& other) noexcept : _sourceBlock(std::exchange(other._sourceBlock, nullptr)), _destinationBlock(std::exchange(other._destinationBlock, nullptr)), _sourcePortDefinition(std::move(other._sourcePortDefinition)), _destinationPortDefinition(std::move(other._destinationPortDefinition)), _state(other._state), _actualBufferSize(other._actualBufferSize), _edgeType(other._edgeType), _sourcePort(std::exchange(other._sourcePort, nullptr)), _destinationPort(std::exchange(other._destinationPort, nullptr)), _minBufferSize(other._minBufferSize), _weight(other._weight), _name(std::move(other._name)) {}

Edge& operator=(Edge&& other) noexcept {
auto tmp = std::move(other);
std::swap(tmp._sourceBlock, _sourceBlock);
std::swap(tmp._destinationBlock, _destinationBlock);
std::swap(tmp._sourcePortDefinition, _sourcePortDefinition);
std::swap(tmp._destinationPortDefinition, _destinationPortDefinition);
std::swap(tmp._state, _state);
std::swap(tmp._actualBufferSize, _actualBufferSize);
std::swap(tmp._edgeType, _edgeType);
std::swap(tmp._sourcePort, _sourcePort);
std::swap(tmp._destinationPort, _destinationPort);

std::swap(tmp._minBufferSize, _minBufferSize);
std::swap(tmp._weight, _weight);
std::swap(tmp._name, _name);

return *this;
}

Edge(BlockModel* sourceBlock, PortDefinition sourcePortDefinition, BlockModel* destinationBlock, PortDefinition destinationPortDefinition, std::size_t minBufferSize, std::int32_t weight, std::string name) : _sourceBlock(sourceBlock), _destinationBlock(destinationBlock), _sourcePortDefinition(sourcePortDefinition), _destinationPortDefinition(destinationPortDefinition), _minBufferSize(minBufferSize), _weight(weight), _name(std::move(name)) {}

Expand Down Expand Up @@ -160,10 +177,10 @@ class BlockModel {
MsgPortInBuiltin* msgIn;
MsgPortOutBuiltin* msgOut;

static std::string_view portName(const DynamicPortOrCollection& portOrCollection) {
static std::string portName(const DynamicPortOrCollection& portOrCollection) {
return std::visit(meta::overloaded{ //
[](const gr::DynamicPort& port) { return port.name; }, //
[](const NamedPortCollection& namedCollection) { return namedCollection.name; }},
[](const NamedPortCollection& namedCollection) { return std::string(namedCollection.name); }},
portOrCollection);
}

Expand Down Expand Up @@ -375,7 +392,7 @@ constexpr bool contains_type = (std::is_same_v<T, Ts> || ...);
template<BlockLike T>
requires std::is_constructible_v<T, property_map>
class BlockWrapper : public BlockModel {
private:
protected:
static_assert(std::is_same_v<T, std::remove_reference_t<T>>);
T _block;
std::string _type_name = gr::meta::type_name<T>();
Expand All @@ -401,36 +418,37 @@ class BlockWrapper : public BlockModel {
msgOut = std::addressof(_block.msgOut);
}

template<typename TPort>
constexpr static auto& processPort(auto& where, TPort& port) noexcept {
where.push_back(gr::DynamicPort(port, DynamicPort::non_owned_reference_tag{}));
return where.back();
}

void dynamicPortLoader() {
void dynamicPortsLoader() {
if (_dynamicPortsLoaded) {
return;
}

auto registerPort = [this]<gr::detail::PortDescription CurrentPortType>(DynamicPorts& where, auto, CurrentPortType*) noexcept {
if constexpr (CurrentPortType::kIsDynamicCollection) {
auto& collection = CurrentPortType::getPortObject(blockRef());
NamedPortCollection result;
result.name = CurrentPortType::Name;
for (auto& port : collection) {
processPort(result.ports, port);
}
where.push_back(std::move(result));
} else {
auto& port = CurrentPortType::getPortObject(blockRef());
port.name = CurrentPortType::Name;
processPort(where, port);
}
auto processPort = []<typename TPort>(auto& where, TPort& port) -> auto& {
where.push_back(gr::DynamicPort(port, DynamicPort::non_owned_reference_tag{}));
return where.back();
};

using Node = std::remove_cvref_t<decltype(blockRef())>;
traits::block::all_input_ports<Node>::for_each(registerPort, _dynamicInputPorts);
traits::block::all_output_ports<Node>::for_each(registerPort, _dynamicOutputPorts);
using TBlock = std::remove_cvref_t<decltype(blockRef())>;
if constexpr (TBlock::blockCategory == block::Category::NormalBlock) {
auto registerPort = [this, processPort]<gr::detail::PortDescription CurrentPortType>(DynamicPorts& where, auto, CurrentPortType*) noexcept {
if constexpr (CurrentPortType::kIsDynamicCollection) {
auto& collection = CurrentPortType::getPortObject(blockRef());
NamedPortCollection result;
result.name = CurrentPortType::Name;
for (auto& port : collection) {
processPort(result.ports, port);
}
where.push_back(std::move(result));
} else {
auto& port = CurrentPortType::getPortObject(blockRef());
port.name = CurrentPortType::Name;
processPort(where, port);
}
};

traits::block::all_input_ports<TBlock>::for_each(registerPort, _dynamicInputPorts);
traits::block::all_output_ports<TBlock>::for_each(registerPort, _dynamicOutputPorts);
}

_dynamicPortsLoaded = true;
}
Expand All @@ -439,7 +457,7 @@ class BlockWrapper : public BlockModel {
BlockWrapper() : BlockWrapper(gr::property_map()) {}
explicit BlockWrapper(gr::property_map initParameter) : _block(std::move(initParameter)) {
initMessagePorts();
_dynamicPortsLoader = std::bind_front(&BlockWrapper::dynamicPortLoader, this);
_dynamicPortsLoader = [this] { this->dynamicPortsLoader(); };
}

BlockWrapper(const BlockWrapper& other) = delete;
Expand Down
1 change: 1 addition & 0 deletions core/include/gnuradio-4.0/Buffer.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef GNURADIO_BUFFER2_H
#define GNURADIO_BUFFER2_H

#include <array>
#include <bit>
#include <concepts>
#include <cstdint>
Expand Down
Loading

0 comments on commit c002328

Please sign in to comment.