Skip to content

Commit e134bf6

Browse files
dynamic outputs 1
1 parent 40cdb92 commit e134bf6

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 *>("Configuration", "A configuration output", configuration_);
@@ -46,6 +49,13 @@ TestNode::TestVariant TestNode::variant() { return variant_; }
4649
* Processing & Validity
4750
*/
4851

52+
// Register dynamic outputs
53+
void TestNode::registerDynamicOutputs()
54+
{
55+
registerDynamicOutput<char>(messageParts_, "Individual character from a message", std::string("Message-Part"));
56+
registerDynamicPointerOutput<char>(messageParts_, "Individual character from a message", std::string("Message-Ptr-Part"));
57+
}
58+
4959
// Perform processing
5060
NodeConstants::ProcessResult TestNode::process()
5161
{
@@ -58,5 +68,9 @@ NodeConstants::ProcessResult TestNode::process()
5868
else
5969
optionalConfiguration_ = std::nullopt;
6070

71+
// Standard dynamic outputs
72+
messageParts_.clear();
73+
messageParts_.insert(messageParts_.end(), message_.begin(), message_.end());
74+
6175
return NodeConstants::ProcessResult::Success;
6276
}

src/nodes/test.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ class TestNode : public Node
4242
// Variant
4343
using TestVariant = VariantParameterData<Structure, Number, std::string, Configuration *>;
4444
TestVariant variant_;
45+
// Test string
46+
char char_;
47+
char *charPtr_;
48+
std::string message_;
49+
std::vector<char> messageParts_;
4550

4651
public:
4752
// Return the optional Configuration
@@ -58,6 +63,9 @@ class TestNode : public Node
5863
/*
5964
* Processing & Validity
6065
*/
66+
private:
67+
void registerDynamicOutputs() override;
68+
6169
public:
6270
// Perform processing
6371
NodeConstants::ProcessResult process() override;

tests/graphData.h

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

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)