Skip to content

Commit

Permalink
Dev message based graph (#245)
Browse files Browse the repository at this point in the history
Reimplements flowgraph alteration to be message-based:

 - Adding blocks should be possible while the graph is runnning (that is, in any state of the graph)
 - Connections should be performed while graph is stopped or paused

This is a follow-up of: #200

Signed-off-by: Ivan Čukić <[email protected]>
  • Loading branch information
ivan-cukic authored Nov 26, 2024
1 parent bb0c188 commit 89dbddc
Show file tree
Hide file tree
Showing 20 changed files with 1,049 additions and 326 deletions.
2 changes: 1 addition & 1 deletion cmake/DependenciesSHAs.cmake
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
set(GIT_SHA_GNURADIO4 f3e8810df421a4669a5f35752dd6c83d78810e24 CACHE STRING "" FORCE) # main as of 2024-11-15
set(GIT_SHA_GNURADIO4 8e18ec00bcb9b74b3b09282c90b5f58104ca5d80 CACHE STRING "" FORCE) # main as of 2024-11-25

set(GIT_SHA_OPENCMW_CPP bb8996babab2000a4ae3612ea146a551a96e59c4 CACHE STRING "" FORCE) # main as of 2024-10-18

Expand Down
9 changes: 5 additions & 4 deletions src/ui/App.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,10 @@ class App {
DashboardPage dashboardPage;
std::shared_ptr<Dashboard> dashboard;
OpenDashboardPage openDashboardPage;
SDLState* sdlState = nullptr;
bool running = true;
ViewMode mainViewMode = ViewMode::VIEW;
SDLState* sdlState = nullptr;
bool running = true;
ViewMode mainViewMode = ViewMode::VIEW;
ViewMode previousViewMode = ViewMode::VIEW;
std::vector<gr::BlockModel*> toolbarBlocks;

components::AppHeader header;
Expand Down Expand Up @@ -142,7 +143,7 @@ class App {

public:
App() {
BlockDefinition::registry().addBlockDefinitionsFromPluginLoader(*pluginLoader);
BlockRegistry::instance().addBlockDefinitionsFromPluginLoader(*pluginLoader);
setStyle(kDefaultStyle);
}

Expand Down
2 changes: 2 additions & 0 deletions src/ui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ add_library(
Flowgraph.hpp
FlowgraphItem.cpp
FlowgraphItem.hpp
GraphModel.cpp
GraphModel.hpp
Dashboard.cpp
Dashboard.hpp
DashboardPage.cpp
Expand Down
8 changes: 6 additions & 2 deletions src/ui/Dashboard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@

#include "App.hpp"
#include "Flowgraph.hpp"
#include "GraphModel.hpp"

#include "utils/Yaml.hpp"

using namespace std::string_literals;

struct FlowgraphMessage {
std::string flowgraph;
std::string layout;
Expand Down Expand Up @@ -239,7 +242,7 @@ std::shared_ptr<Dashboard> Dashboard::create(FlowGraphItem* fgItem, const std::s
Block* Dashboard::createSink() {
const auto sinkCount = std::ranges::count_if(localFlowGraph.blocks(), [](const auto& b) { return b->type().isPlotSink(); });
auto name = fmt::format("sink {}", sinkCount + 1);
auto sink = BlockDefinition::registry().get("opendigitizer::ImPlotSink")->createBlock(name);
auto sink = BlockRegistry::instance().get("opendigitizer::ImPlotSink")->createBlock(name);
sink->updateSettings({{"color", randomColor()}});
auto sinkptr = sink.get();
localFlowGraph.addBlock(std::move(sink));
Expand All @@ -251,7 +254,8 @@ void Dashboard::setNewDescription(const std::shared_ptr<DashboardDescription>& d
void Dashboard::load() {
if (m_desc->source != unsavedSource()) {
fetch(
m_desc->source, m_desc->filename, {What::Flowgraph, What::Dashboard}, [_this = shared()](std::array<std::string, 2>&& data) { _this->load(std::move(data[0]), std::move(data[1])); },
m_desc->source, m_desc->filename, {What::Flowgraph, What::Dashboard}, //
[_this = shared()](std::array<std::string, 2>&& data) { _this->load(std::move(data[0]), std::move(data[1])); },
[_this = shared()]() {
auto error = fmt::format("Invalid flowgraph for dashboard {}/{}", _this->m_desc->source->path, _this->m_desc->filename);
components::Notification::error(error);
Expand Down
6 changes: 4 additions & 2 deletions src/ui/DashboardPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,8 @@ void DashboardPage::drawPlots(Dashboard& dashboard, DigitizerUi::DashboardPage::
plotItemHovered = true;

if (ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
m_editPane.block = dashboard.localFlowGraph.findBlock(s->blockName);
// TODO(NOW) which node was clicked -- it needs to have a sane way to get this?
m_editPane.block = nullptr; // dashboard.localFlowGraph.findBlock(s->blockName);
m_editPane.closeTime = std::chrono::system_clock::now() + LookAndFeel::instance().editPaneCloseDelay;
}

Expand Down Expand Up @@ -548,6 +549,7 @@ void DashboardPage::drawGrid(float w, float h) {
}

void DashboardPage::drawLegend(Dashboard& dashboard, const DashboardPage::Mode& mode) noexcept {
#ifdef TODO_PORT
alignForWidth(std::max(10.f, legend_box.x), 0.5f);
legend_box.x = 0.f;
{
Expand Down Expand Up @@ -612,6 +614,7 @@ void DashboardPage::drawLegend(Dashboard& dashboard, const DashboardPage::Mode&
}
}
// end draw legend
#endif
}

void DashboardPage::newPlot(Dashboard& dashboard) {
Expand Down Expand Up @@ -682,7 +685,6 @@ void DashboardPage::newPlot(Dashboard& dashboard) {
void DashboardPage::addSignalCallback(Dashboard& dashboard, Block* block) {
ImGui::CloseCurrentPopup();
auto* newsink = dashboard.createSink();
// newPlot(dashboard);
dashboard.localFlowGraph.connect(&block->outputs()[0], &newsink->inputs()[0]);
newPlot(dashboard);
auto source = std::ranges::find_if(dashboard.sources(), [newsink](const auto& s) { return s.blockName == newsink->name; });
Expand Down
44 changes: 24 additions & 20 deletions src/ui/Flowgraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,23 +74,24 @@ std::unique_ptr<Block> BlockDefinition::createBlock(std::string_view name) const
return std::make_unique<Block>(name, this, std::move(params));
}

BlockDefinition::Registry& BlockDefinition::registry() {
static Registry r;
BlockRegistry& BlockRegistry::instance() {
static BlockRegistry r;
return r;
}

const BlockDefinition* BlockDefinition::Registry::get(std::string_view id) const {
auto it = m_types.find(id);
return it == m_types.end() ? nullptr : it->second.get();
const BlockDefinition* BlockRegistry::get(std::string_view id) const {
auto it = _types.find(id);
return it == _types.end() ? nullptr : it->second.get();
}

void BlockDefinition::Registry::addBlockDefinitionsFromPluginLoader(gr::PluginLoader& pluginLoader) {
void BlockRegistry::addBlockDefinitionsFromPluginLoader(gr::PluginLoader& pluginLoader) {
for (const auto& typeName : pluginLoader.knownBlocks()) {
const auto availableParametrizations = pluginLoader.knownBlockParameterizations(typeName);
auto type = std::make_unique<BlockDefinition>(typeName, typeName, "TODO category");
bool first = true;
auto type = std::make_unique<BlockDefinition>(typeName, typeName, "TODO category");
bool first = true;

for (const auto& parametrization : availableParametrizations) {
std::ranges::transform(pluginLoader.knownBlockParameterizations(typeName), std::back_inserter(type->availableParametrizations), [](std::string_view in) { return std::string(in); });

for (const auto& parametrization : type->availableParametrizations) {
BlockInstantiationDefinition instantiationType;

auto prototype = pluginLoader.instantiate(typeName, parametrization);
Expand Down Expand Up @@ -134,7 +135,7 @@ void BlockDefinition::Registry::addBlockDefinitionsFromPluginLoader(gr::PluginLo
}
}

void BlockDefinition::Registry::addBlockDefinition(std::unique_ptr<BlockDefinition>&& t) { m_types.insert({t->name, std::move(t)}); }
void BlockRegistry::addBlockDefinition(std::unique_ptr<BlockDefinition>&& t) { _types.insert({t->name, std::move(t)}); }

Block::Block(std::string_view name, const BlockDefinition* t, gr::property_map settings) : name(name), m_type(t), m_settings(std::move(settings)) { //
setCurrentInstantiation(m_type->instantiations.cbegin()->first);
Expand Down Expand Up @@ -321,18 +322,18 @@ void FlowGraph::parse(const std::filesystem::path& file) {
void FlowGraph::parse(const std::string& str) {
clear();

auto graph = [this, &str]() {
auto grGraph = [this, &str]() {
try {
return gr::loadGrc(*_pluginLoader, str);
} catch (const std::string& e) {
throw std::runtime_error(e);
}
}();

graph.forEachBlock([&](const auto& grBlock) {
grGraph.forEachBlock([&](const auto& grBlock) {
auto typeName = grBlock.typeName();
typeName = std::string_view(typeName.begin(), typeName.find('<'));
auto type = BlockDefinition::registry().get(typeName);
auto type = BlockRegistry::instance().get(typeName);
if (!type) {
auto msg = fmt::format("Block type '{}' is unknown.", typeName);
components::Notification::error(msg);
Expand Down Expand Up @@ -391,7 +392,7 @@ void FlowGraph::parse(const std::string& str) {
portDefinition.definition);
};

graph.forEachEdge([&](const auto& edge) {
grGraph.forEachEdge([&](const auto& edge) {
auto srcBlock = findBlock(edge._sourceBlock->uniqueName());
assert(srcBlock);
// TODO do not ignore subindexes
Expand All @@ -403,6 +404,7 @@ void FlowGraph::parse(const std::string& str) {
if (srcPort == srcBlock->m_outputs.end() || dstPort == dstBlock->m_inputs.end()) {
return;
}

connect(&*srcPort, &*dstPort);
});

Expand Down Expand Up @@ -579,7 +581,7 @@ std::string_view sourceTypeForURI(std::string_view uriStr) {
} // namespace

Block* FlowGraph::addRemoteSource(std::string_view uriStr) {
auto block = BlockDefinition::registry().get(sourceTypeForURI(uriStr))->createBlock("Remote Source");
auto block = BlockRegistry::instance().get(sourceTypeForURI(uriStr))->createBlock("Remote Source");
block->updateSettings({{"remote_uri", std::string(uriStr)}});
auto res = block.get();
addBlock(std::move(block));
Expand All @@ -602,12 +604,12 @@ ExecutionContext FlowGraph::createExecutionContext() {
if (isDrawable(block->metaInformation(), "ChartPane")) {
context.plotSinkGrBlocks.insert({block->name, grBlock.get()});
}
context.graph.addBlock(std::move(grBlock));
context.grGraph.addBlock(std::move(grBlock));
}

auto findBlock = [&](std::string_view name) -> gr::BlockModel* {
const auto it = std::ranges::find_if(context.graph.blocks(), [&](auto& b) { return b->uniqueName() == name; });
return it == context.graph.blocks().end() ? nullptr : it->get();
const auto it = std::ranges::find_if(context.grGraph.blocks(), [&](auto& b) { return b->uniqueName() == name; });
return it == context.grGraph.blocks().end() ? nullptr : it->get();
};

for (auto& c : m_connections) {
Expand All @@ -617,7 +619,7 @@ ExecutionContext FlowGraph::createExecutionContext() {
continue;
}

context.graph.connect(*src, c.src.index, *dst, c.dst.index);
context.grGraph.connect(*src, c.src.index, *dst, c.dst.index);
}

m_graphChanged = false;
Expand All @@ -630,6 +632,8 @@ ExecutionContext FlowGraph::createExecutionContext() {
}

void FlowGraph::handleMessage(const gr::Message& msg) {
graphModel.processMessage(msg);

if (msg.serviceName != App::instance().schedulerUniqueName() && msg.endpoint == gr::block::property::kSetting) {
const auto it = std::ranges::find_if(m_blocks, [&](const auto& b) { return b->m_uniqueName == msg.serviceName; });
if (it == m_blocks.end()) {
Expand Down
51 changes: 26 additions & 25 deletions src/ui/Flowgraph.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include "GraphModel.hpp"
#include <filesystem>
#include <functional>
#include <string_view>
Expand Down Expand Up @@ -203,11 +204,11 @@ class BlockInstantiationDefinition {
};

struct BlockDefinition {
const std::string name;
const std::string label;
std::vector<DataType> availableParametrizations;
const std::string category;
gr::property_map defaultSettings;
const std::string name;
const std::string label;
std::vector<std::string> availableParametrizations;
const std::string category;
gr::property_map defaultSettings;

// We are going to assume that source/sink doesn't depend on
// template parametrization
Expand All @@ -226,31 +227,31 @@ struct BlockDefinition {
// TODO make this smarter once metaInformation() is statically available
return name == "opendigitizer::ImPlotSink" || name == "opendigitizer::ImPlotSinkDataSet";
}
};

// TODO: Move to a separate class
struct Registry {
void addBlockDefinitionsFromPluginLoader(gr::PluginLoader& pluginLoader);

void addBlockDefinition(std::unique_ptr<BlockDefinition>&& t);
// TODO: Remove once we have message-based registry queries
struct BlockRegistry {
void addBlockDefinitionsFromPluginLoader(gr::PluginLoader& pluginLoader);

const BlockDefinition* get(std::string_view id) const;
void addBlockDefinition(std::unique_ptr<BlockDefinition>&& t);

inline const auto& types() const { return m_types; }
const BlockDefinition* get(std::string_view id) const;

private:
// This stuff is to enable looking up in the m_types map with string_view
template<typename... Keys>
struct transparent_hash : std::hash<Keys>... {
using is_transparent = void;
using std::hash<Keys>::operator()...;
};
inline const auto& types() const { return _types; }

using transparent_string_hash = transparent_hash<std::string, std::string_view, const char*, char*>;
static BlockRegistry& instance();

std::unordered_map<std::string, std::unique_ptr<BlockDefinition>, transparent_string_hash, std::equal_to<>> m_types;
private:
// This stuff is to enable looking up in the _types map with string_view
template<typename... Keys>
struct transparent_hash : std::hash<Keys>... {
using is_transparent = void;
using std::hash<Keys>::operator()...;
};

static Registry& registry();
using transparent_string_hash = transparent_hash<std::string, std::string_view, const char*, char*>;

std::unordered_map<std::string, std::unique_ptr<BlockDefinition>, transparent_string_hash, std::equal_to<>> _types;
};

class Block {
Expand All @@ -272,8 +273,6 @@ class Block {
std::vector<Connection*> portConnections;
};

class OutputPort : public Port {};

struct EnumParameter {
const BlockInstantiationDefinition::EnumParameter& definition;
int optionIndex;
Expand Down Expand Up @@ -375,7 +374,7 @@ class Connection {
};

struct ExecutionContext {
gr::Graph graph;
gr::Graph grGraph;
std::unordered_map<std::string, gr::BlockModel*> plotSinkGrBlocks;
std::vector<gr::BlockModel*> toolbarBlocks;
};
Expand Down Expand Up @@ -434,6 +433,8 @@ class FlowGraph {

void changeBlockDefinition(Block* block, const std::string& type);

UiGraphModel graphModel;

private:
std::shared_ptr<gr::PluginLoader> _pluginLoader;
std::vector<std::unique_ptr<Block>> m_blocks;
Expand Down
Loading

0 comments on commit 89dbddc

Please sign in to comment.