Skip to content

Commit c6dfc3c

Browse files
dynamic outputs 1
1 parent 18c6adc commit c6dfc3c

7 files changed

Lines changed: 152 additions & 0 deletions

File tree

‎src/nodes/node.cpp‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ NodeConstants::ProcessResult Node::run()
140140
case (NodeConstants::ProcessResult::Failed):
141141
break;
142142
case (NodeConstants::ProcessResult::Success):
143+
updateDynamicOutputs();
143144
++versionIndex_;
144145
upToDate_ = true;
145146
break;
@@ -282,6 +283,12 @@ std::shared_ptr<ParameterBase> Node::findOption(std::string_view optionName) con
282283
return options_.at(std::string{optionName});
283284
}
284285

286+
// Update dynamic outputs
287+
void Node::updateDynamicOutputs() { registerDynamicOutputs(); }
288+
289+
// Register dynamic outputs
290+
void Node::registerDynamicOutputs() {}
291+
285292
// Return Options
286293
Node::NodeParameterMap &Node::options() { return options_; };
287294

‎src/nodes/node.h‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,46 @@ class Node : public Serialisable<>
300300
}
301301
// Return named option if it exists
302302
std::shared_ptr<ParameterBase> findOption(std::string_view name) const;
303+
// Update dynamic outputs
304+
void updateDynamicOutputs();
305+
// Register dynamic output from container data source
306+
template <typename T>
307+
void registerDynamicOutput(std::vector<T> &data, std::string description,
308+
const std::variant<std::string, std::function<std::string(T)>> &name = std::string("dynOut"))
309+
{
310+
for (int i = 0; i < data.size(); i++)
311+
{
312+
auto val = data[i];
313+
auto paramName = std::holds_alternative<std::string>(name) ? (std::get<std::string>(name) + std::format("-{}", i))
314+
: std::get<std::function<std::string(T)>>(name)(val);
315+
316+
// Check if output already exists - do not add if it does
317+
if (outputs_.find(paramName) != outputs_.end())
318+
continue;
319+
320+
addOutput<T>(paramName, description, data[i]);
321+
}
322+
}
323+
template <typename T>
324+
void
325+
registerDynamicPointerOutput(std::vector<T> &data, std::string description,
326+
const std::variant<std::string, std::function<std::string(T)>> &name = std::string("dynOut"))
327+
{
328+
for (int i = 0; i < data.size(); i++)
329+
{
330+
auto val = data[i];
331+
auto paramName = std::holds_alternative<std::string>(name) ? (std::get<std::string>(name) + std::format("-{}", i))
332+
: std::get<std::function<std::string(T)>>(name)(val);
333+
334+
// Check if output already exists - do not add if it does
335+
if (outputs_.find(paramName) != outputs_.end())
336+
continue;
337+
338+
addPointerOutput<T>(paramName, description, data[i]);
339+
}
340+
}
341+
// Register dynamic outputs
342+
virtual void registerDynamicOutputs();
303343
// Return options
304344
NodeParameterMap &options();
305345
// Set option value

‎src/nodes/parameter.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ class ParameterBase : public Serialisable<>
5353
ClearData, /* Indicates that any local data should be cleared if the parameter is changed */
5454
Input, /* Indicates that the parameter is meant to be a sink for data and not a source */
5555
Output, /* Indicates that the parameter is meant to be a source of data and not a sink */
56+
Dynamic, /* Indicates that the parameter is meant to be a dynamic source of data and not a sink */
5657
};
5758
// Allowed Edge Count
5859
enum AllowedEdgeCount

‎src/nodes/test.cpp‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ TestNode::TestNode(Graph *parentGraph) : Node(parentGraph)
1212
addInput("NumberVector", "A vector of numbers", numberVector_);
1313
addInput("OptionalNumber", "A single number", optionalNumber_);
1414
addInput("Variant", "A variant", variant_);
15+
addInput("Message", "A message", message_);
16+
addInput("Char", "A character", char_);
17+
addInput("CharPtr", "A character", charPtr_);
1518

1619
// Outputs
1720
addOutput("Configuration", "A configuration output", configuration_);
@@ -42,6 +45,13 @@ TestNode::TestVariant TestNode::variant() { return variant_; }
4245
* Processing
4346
*/
4447

48+
// Register dynamic outputs
49+
void TestNode::registerDynamicOutputs()
50+
{
51+
registerDynamicOutput<char>(messageParts_, "Individual character from a message", std::string("Message-Part"));
52+
registerDynamicPointerOutput<char>(messageParts_, "Individual character from a message", std::string("Message-Ptr-Part"));
53+
}
54+
4555
// Perform processing
4656
NodeConstants::ProcessResult TestNode::process()
4757
{
@@ -54,5 +64,9 @@ NodeConstants::ProcessResult TestNode::process()
5464
else
5565
optionalConfiguration_ = std::nullopt;
5666

67+
// Standard dynamic outputs
68+
messageParts_.clear();
69+
messageParts_.insert(messageParts_.end(), message_.begin(), message_.end());
70+
5771
return NodeConstants::ProcessResult::Success;
5872
}

‎src/nodes/test.h‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ class TestNode : public Node
3333
// Variant
3434
using TestVariant = VariantParameterData<Structure, Number, std::string, Configuration *>;
3535
TestVariant variant_;
36+
// Test string
37+
char char_;
38+
char *charPtr_;
39+
std::string message_;
40+
std::vector<char> messageParts_;
3641

3742
public:
3843
// Return type of the node
@@ -53,6 +58,9 @@ class TestNode : public Node
5358
/*
5459
* Processing
5560
*/
61+
private:
62+
void registerDynamicOutputs() override;
63+
5664
protected:
5765
// Perform processing
5866
NodeConstants::ProcessResult process() override;

‎tests/graphData.h‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,22 @@ class TestGraph : public DissolveGraph
5959
std::string fetchHeadName() const { return head_ ? std::string(head_->name()) : "NO_NODE"; }
6060
// Returns reference to current top node in graph, cast to the known node type
6161
template <class NodeType> NodeType *head() const { return static_cast<NodeType *>(head_); }
62+
// Run the graph in a piecewise manner - initially from a specific node, then from the last node - in order to emplace a set
63+
// of dynamic edges that we expect to exist at run time
64+
NodeConstants::ProcessResult runDynamic(Node *startNode, std::vector<EdgeDefinition> edges)
65+
{
66+
setUpdateRequired();
67+
68+
auto result = NodeConstants::ProcessResult::Unchanged;
69+
result = startNode->run();
70+
if (result == NodeConstants::ProcessResult::Failed)
71+
return result;
72+
73+
for (const auto &edge : edges)
74+
if (!addEdge(edge) || findNode(edge.targetNode)->run() == NodeConstants::ProcessResult::Failed)
75+
return NodeConstants::ProcessResult::Failed;
76+
return result;
77+
}
6278
// Append new node to the graph
6379
Node *appendNode(const std::string &nodeType, const std::optional<std::string> &name = {})
6480
{

‎tests/nodes/parameters.cpp‎

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,4 +360,70 @@ TEST_F(ParametersTest, OptionalPointerToVariant)
360360
EXPECT_EQ(std::get<Configuration *>(b_->variant().data), &a_->optionalConfiguration().value());
361361
}
362362

363+
TEST_F(ParametersTest, DynamicOutput)
364+
{
365+
ASSERT_TRUE(testGraph_.addNode(std::make_unique<TestNode>(&testGraph_), "Sender"));
366+
ASSERT_TRUE(testGraph_.addNode(std::make_unique<TestNode>(&testGraph_), "RecieverA"));
367+
ASSERT_TRUE(testGraph_.addNode(std::make_unique<TestNode>(&testGraph_), "RecieverB"));
368+
ASSERT_TRUE(testGraph_.addNode(std::make_unique<TestNode>(&testGraph_), "RecieverC"));
369+
ASSERT_TRUE(testGraph_.addNode(std::make_unique<TestNode>(&testGraph_), "RecieverD"));
370+
ASSERT_TRUE(testGraph_.addNode(std::make_unique<TestNode>(&testGraph_), "RecieverE"));
371+
372+
auto sender = testGraph_.findNode("Sender");
373+
ASSERT_TRUE(sender->setInput("Message", std::string("hello")));
374+
ASSERT_EQ(testGraph_.runDynamic(sender,
375+
{
376+
{"Sender", "Message-Part-0", "RecieverA", "Char"},
377+
{"Sender", "Message-Part-1", "RecieverB", "Char"},
378+
{"Sender", "Message-Part-2", "RecieverC", "Char"},
379+
{"Sender", "Message-Part-3", "RecieverD", "Char"},
380+
{"Sender", "Message-Part-4", "RecieverE", "Char"},
381+
382+
}),
383+
NodeConstants::ProcessResult::Success);
384+
385+
std::vector<char> chars;
386+
for (const auto &which : {"A", "B", "C", "D", "E"})
387+
{
388+
auto node = testGraph_.findNode("Reciever" + std::string(which));
389+
chars.push_back(node->findInput("Char")->get<char>());
390+
}
391+
392+
std::string message(chars.begin(), chars.end());
393+
ASSERT_EQ(message, "hello");
394+
}
395+
396+
TEST_F(ParametersTest, DynamicPointerOutput)
397+
{
398+
ASSERT_TRUE(testGraph_.addNode(std::make_unique<TestNode>(&testGraph_), "Sender"));
399+
ASSERT_TRUE(testGraph_.addNode(std::make_unique<TestNode>(&testGraph_), "RecieverA"));
400+
ASSERT_TRUE(testGraph_.addNode(std::make_unique<TestNode>(&testGraph_), "RecieverB"));
401+
ASSERT_TRUE(testGraph_.addNode(std::make_unique<TestNode>(&testGraph_), "RecieverC"));
402+
ASSERT_TRUE(testGraph_.addNode(std::make_unique<TestNode>(&testGraph_), "RecieverD"));
403+
ASSERT_TRUE(testGraph_.addNode(std::make_unique<TestNode>(&testGraph_), "RecieverE"));
404+
405+
auto sender = testGraph_.findNode("Sender");
406+
ASSERT_TRUE(sender->setInput("Message", std::string("hello")));
407+
ASSERT_EQ(testGraph_.runDynamic(sender,
408+
{
409+
{"Sender", "Message-Ptr-Part-0", "RecieverA", "CharPtr"},
410+
{"Sender", "Message-Ptr-Part-1", "RecieverB", "CharPtr"},
411+
{"Sender", "Message-Ptr-Part-2", "RecieverC", "CharPtr"},
412+
{"Sender", "Message-Ptr-Part-3", "RecieverD", "CharPtr"},
413+
{"Sender", "Message-Ptr-Part-4", "RecieverE", "CharPtr"},
414+
415+
}),
416+
NodeConstants::ProcessResult::Success);
417+
418+
std::vector<char> chars;
419+
for (const auto &which : {"A", "B", "C", "D", "E"})
420+
{
421+
auto node = testGraph_.findNode("Reciever" + std::string(which));
422+
chars.push_back(*node->findInput("CharPtr")->get<char *>());
423+
}
424+
425+
std::string message(chars.begin(), chars.end());
426+
ASSERT_EQ(message, "hello");
427+
}
428+
363429
} // namespace UnitTest

0 commit comments

Comments
 (0)