satdump/src-core/dsp/flowgraph/flowgraph.cpp

382 lines
14 KiB
C++
Raw Permalink Normal View History

2025-03-09 10:16:40 +01:00
#include "flowgraph.h"
#include "core/exception.h"
2025-05-02 10:31:47 +02:00
#include <chrono>
2025-04-15 19:06:12 +01:00
#include <limits>
2025-03-09 10:16:40 +01:00
#include "imgui/imnodes/imnodes.h"
#include "imgui/imnodes/imnodes_internal.h"
#include "logger.h"
2025-05-26 19:21:10 +01:00
#include "utils/string.h"
2025-03-09 10:16:40 +01:00
#include "dsp/path/splitter.h"
2025-04-15 19:06:12 +01:00
2025-05-02 10:31:47 +02:00
#include <thread>
2025-04-21 23:17:21 +02:00
2025-03-09 10:16:40 +01:00
namespace satdump
{
namespace ndsp
{
2025-04-15 19:06:12 +01:00
Flowgraph::Flowgraph() {}
2025-03-09 10:16:40 +01:00
2025-04-15 19:06:12 +01:00
Flowgraph::~Flowgraph() {}
2025-03-09 10:16:40 +01:00
int Flowgraph::getNewNodeID()
{
for (int i = 0; i < std::numeric_limits<int>::max(); i++)
{
bool already_contained = false;
for (auto &n : nodes)
if (n->id == i)
already_contained = true;
if (already_contained)
continue;
return i;
}
throw satdump_exception("No valid ID found for new node ID!");
}
int Flowgraph::getNewNodeIOID(std::vector<Node::InOut> *ptr)
{
for (int i = 0; i < std::numeric_limits<int>::max(); i++)
{
bool already_contained = false;
for (auto &n : nodes)
for (auto &io : n->node_io)
if (io.id == i)
already_contained = true;
if (ptr != nullptr)
for (auto &io : *ptr)
if (io.id == i)
already_contained = true;
if (already_contained)
continue;
return i;
}
throw satdump_exception("No valid ID found for new node IO ID!");
}
int Flowgraph::getNewLinkID()
{
for (int i = 0; i < std::numeric_limits<int>::max(); i++)
{
bool already_contained = false;
for (auto &l : links)
if (l.id == i)
already_contained = true;
if (already_contained)
continue;
return i;
}
throw satdump_exception("No valid ID found for new link ID!");
}
void Flowgraph::renderAddMenu(std::pair<const std::string, NodeInternalReg> &opt, std::vector<std::string> cats, int pos)
{
if (pos == (cats.size() - 1))
{
if (ImGui::MenuItem(cats[pos].c_str()))
{
auto mpos = ImGui::GetMousePos();
auto ptr = addNode(opt.first, opt.second.func(this));
ptr->pos_was_set = true;
ImNodes::SetNodeScreenSpacePos(ptr->id, mpos);
}
}
else
{
if (ImGui::BeginMenu(cats[pos].c_str()))
{
renderAddMenu(opt, cats, pos + 1);
ImGui::EndMenu();
}
}
}
void Flowgraph::render()
{
ImNodes::PushAttributeFlag(ImNodesAttributeFlags_EnableLinkDetachWithDragClick);
ImNodes::BeginNodeEditor();
ImNodes::MiniMap();
// ImNodes::PushColorStyle(ImNodesCol_TitleBar, 0xc01c28FF);
for (auto &n : nodes)
{
ImNodes::BeginNode(n->id);
ImNodes::BeginNodeTitleBar();
2025-03-16 18:08:49 +01:00
ImGui::Text("%s", n->title.c_str());
2025-03-09 10:16:40 +01:00
ImNodes::EndNodeTitleBar();
2025-04-21 23:17:21 +02:00
if (n->internal->render())
n->updateIO(); // TODOREWORK!
2025-03-09 10:16:40 +01:00
for (auto &io : n->node_io)
{
if (io.is_out)
{
ImNodes::BeginOutputAttribute(io.id);
// const float node_width = 200.0 * ui_scale;
// const float label_width = ImGui::CalcTextSize(io.name.c_str()).x;
// ImGui::Indent(node_width - label_width);
2025-03-16 18:08:49 +01:00
ImGui::Text("%s", io.name.c_str());
2025-03-09 10:16:40 +01:00
ImNodes::EndOutputAttribute();
}
else
{
ImNodes::BeginInputAttribute(io.id);
2025-03-16 18:08:49 +01:00
ImGui::Text("%s", io.name.c_str());
2025-03-09 10:16:40 +01:00
ImNodes::EndInputAttribute();
}
}
ImNodes::EndNode();
if (!n->pos_was_set)
{
ImNodes::SetNodeGridSpacePos(n->id, {n->pos_x, n->pos_y});
n->pos_was_set = true;
}
auto pos = ImNodes::GetNodeGridSpacePos(n->id);
n->pos_x = pos.x;
n->pos_y = pos.y;
}
// ImNodes::PopColorStyle();
for (auto &l : links)
ImNodes::Link(l.id, l.start, l.end);
ImNodes::EndNodeEditor();
int start_att, end_att;
if (ImNodes::IsLinkCreated(&start_att, &end_att))
{
links.push_back({getNewLinkID(), start_att, end_att});
logger->trace("LINK CREATE %d %d", start_att, end_att);
}
int link_id;
if (ImNodes::IsLinkDestroyed(&link_id))
{
2025-04-15 19:06:12 +01:00
auto iter = std::find_if(links.begin(), links.end(), [link_id](const Link &link) -> bool { return link.id == link_id; });
2025-03-09 10:16:40 +01:00
logger->trace("LINK DELETE %d %d", iter->start, iter->end);
links.erase(iter);
}
if (ImGui::IsKeyPressed(ImGuiKey_Delete))
{
int node_s = ImNodes::NumSelectedNodes();
if (node_s > 0)
{
std::vector<int> nodes_ids(node_s);
ImNodes::GetSelectedNodes(nodes_ids.data());
for (auto &id : nodes_ids)
{
2025-04-15 19:06:12 +01:00
auto iter = std::find_if(nodes.begin(), nodes.end(), [id](const std::shared_ptr<Node> &node) -> bool { return node->id == id; });
2025-03-09 10:16:40 +01:00
logger->trace("NODE DELETE %d", id);
for (auto &linkid : iter->get()->node_io)
{
2025-04-15 19:06:12 +01:00
auto liter = std::find_if(links.begin(), links.end(), [linkid](const Link &link) -> bool { return link.start == linkid.id || link.end == linkid.id; });
2025-03-09 10:16:40 +01:00
if (liter != links.end())
links.erase(liter);
}
nodes.erase(iter);
}
}
}
if (ImGui::IsMouseClicked(ImGuiMouseButton_Right))
ImGui::OpenPopup("##popuprightclickflowgraph");
if (ImGui::BeginPopup("##popuprightclickflowgraph"))
{
if (ImGui::BeginMenu("Add Node"))
{
for (auto &opt : node_internal_registry)
{
std::vector<std::string> cats = splitString(opt.second.menuname, '/');
renderAddMenu(opt, cats, 0);
}
ImGui::EndMenu();
}
ImGui::EndPopup();
}
}
void Flowgraph::run()
{
is_running = true;
try
{
// Holds stuff such as splitters when one output goes to more than
// one input
std::vector<std::shared_ptr<ndsp::Block>> additional_blocks;
// Iterate through all nodes
for (auto &n : nodes)
{
2025-04-15 19:06:12 +01:00
// n->internal->applyP(); // TODOREWORK?
2025-03-09 10:16:40 +01:00
auto &blk = n->internal->blk;
// Iterate through outputs
2025-03-24 17:59:58 +01:00
for (int o = 0; o < blk->get_outputs().size(); o++)
2025-03-09 10:16:40 +01:00
{
// Get output ID
int o_id = -1;
for (int c = 0, oc = 0; c < n->node_io.size(); c++)
{
if (n->node_io[c].is_out)
{
if (oc == o)
o_id = n->node_io[c].id;
oc++;
}
}
// Iterate through links, to asign outputs to applicable inputs
2025-03-24 17:59:58 +01:00
struct InputH
{
std::shared_ptr<ndsp::Block> blk;
int idx;
};
std::vector<InputH> inputs_to_feed;
2025-03-09 10:16:40 +01:00
for (auto &l : links)
{
if (l.start == o_id)
{
// Iterate through nodes to find valid inputs
for (auto &n2 : nodes)
{
for (int b = 0, b2 = 0; b < n2->node_io.size(); b++)
{
if (!n2->node_io[b].is_out)
{
if (n2->node_io[b].id == l.end)
{
// n2->internal->blk->inputs[b2] = blk->outputs[o];
2025-03-24 17:59:58 +01:00
inputs_to_feed.push_back({n2->internal->blk, b2}); // &n2->internal->blk->get_output(b2, 16 /*TODOREWORK*/));
2025-03-09 10:16:40 +01:00
logger->trace("Assigned to : " + n2->internal->blk->d_id);
}
b2++;
}
}
}
}
if (l.end == o_id)
{
// Iterate through nodes to find valid inputs
for (auto &n2 : nodes)
{
for (int b = 0, b2 = 0; b < n2->node_io.size(); b++)
{
if (!n2->node_io[b].is_out)
{
if (n2->node_io[b].id == l.start)
{
// n2->internal->blk->inputs[b2] = blk->outputs[o];
2025-03-24 17:59:58 +01:00
inputs_to_feed.push_back({n2->internal->blk, b2}); // inputs_to_feed.push_back(&n2->internal->blk->get_output(b2, 16 /*TODOREWORK*/));
2025-03-09 10:16:40 +01:00
logger->trace("Assigned to : " + n2->internal->blk->d_id);
}
b2++;
}
}
}
}
}
if (inputs_to_feed.size() == 1)
{
2025-03-24 17:59:58 +01:00
inputs_to_feed[0].blk->link(blk.get(), o, inputs_to_feed[0].idx, 16 /*TODOREWORK*/); // = blk->get_output(o, 16 /*TODOREWORK*/);
2025-03-09 10:16:40 +01:00
}
else if (inputs_to_feed.size() > 1)
{
logger->error("More than one to connect! Adding splitter");
std::shared_ptr<ndsp::Block> ptr;
2025-03-24 17:59:58 +01:00
if (blk->get_output(o, 0).type == ndsp::BlockIOType::DSP_SAMPLE_TYPE_CF32)
2025-03-09 10:16:40 +01:00
ptr = std::make_shared<ndsp::SplitterBlock<complex_t>>();
2025-03-24 17:59:58 +01:00
else if (blk->get_output(o, 0).type == ndsp::BlockIOType::DSP_SAMPLE_TYPE_F32)
2025-03-09 10:16:40 +01:00
ptr = std::make_shared<ndsp::SplitterBlock<float>>();
else
throw satdump_exception("Unsupported splitter block IO type");
nlohmann::json p;
p["noutputs"] = inputs_to_feed.size();
2025-03-17 20:24:12 +01:00
ptr->set_cfg(p);
2025-03-09 10:16:40 +01:00
2025-03-24 17:59:58 +01:00
ptr->link(blk.get(), o, 0, 16 /*TODOREWORK*/); // ptr->inputs[0] = blk->outputs[o];
2025-03-09 10:16:40 +01:00
for (int v = 0; v < inputs_to_feed.size(); v++)
2025-03-24 17:59:58 +01:00
inputs_to_feed[v].blk->link(ptr.get(), v, inputs_to_feed[0].idx, 16 /*TODOREWORK*/); //(*inputs_to_feed[v]) = ptr->outputs[v];
2025-03-09 10:16:40 +01:00
additional_blocks.push_back(ptr);
}
else if (inputs_to_feed.size() == 0)
{
throw satdump_exception("Block has unconnected output!");
}
}
}
// Start them all
for (auto &n : nodes)
n->internal->blk->start();
for (auto &b : additional_blocks)
b->start();
2025-04-21 23:17:21 +02:00
for (auto &n : nodes)
n->internal->up_state();
2025-03-09 10:16:40 +01:00
// And then wait for them to exit
for (auto &n : nodes)
n->internal->blk->stop();
for (auto &b : additional_blocks)
b->stop();
2025-04-21 23:17:21 +02:00
2025-05-02 10:31:47 +02:00
std::this_thread::sleep_for(std::chrono::seconds(2));
2025-04-21 23:17:21 +02:00
for (auto &n : nodes)
n->internal->up_state();
2025-03-09 10:16:40 +01:00
}
catch (std::exception &e)
{
logger->error("Error running flowgraph : %s", e.what());
}
is_running = false;
}
void Flowgraph::stop()
{
try
{
2025-03-10 20:05:31 +01:00
// Iterate through all nodes
for (auto &n : nodes)
{
// Stop only those that are sources
2025-03-24 17:59:58 +01:00
if (n->internal->blk->get_inputs().size() == 0)
2025-03-10 20:05:31 +01:00
{
logger->trace("Stopping source " + n->internal->blk->d_id);
n->internal->blk->stop(true);
logger->trace("Stopped source " + n->internal->blk->d_id);
}
}
2025-03-09 10:16:40 +01:00
}
catch (std::exception &e)
{
logger->error("Error running flowgraph : %s", e.what());
}
}
2025-04-15 19:06:12 +01:00
} // namespace ndsp
} // namespace satdump