diff options
-rw-r--r-- | core/src/Model/Workflow.cpp | 150 | ||||
-rw-r--r-- | core/src/Model/Workflow.hpp | 5 | ||||
-rw-r--r-- | core/src/Model/WorkflowNodes.cpp | 30 | ||||
-rw-r--r-- | core/src/Model/WorkflowNodes.hpp | 4 | ||||
-rw-r--r-- | core/src/Model/fwd.hpp | 2 | ||||
-rw-r--r-- | core/src/UI/UI_Workflows.cpp | 70 |
6 files changed, 225 insertions, 36 deletions
diff --git a/core/src/Model/Workflow.cpp b/core/src/Model/Workflow.cpp index 588081d..7ca1b9e 100644 --- a/core/src/Model/Workflow.cpp +++ b/core/src/Model/Workflow.cpp @@ -2,6 +2,7 @@ #include <algorithm> #include <cassert> +#include <queue> #include <utility> WorkflowConnection::WorkflowConnection() @@ -243,7 +244,10 @@ bool Workflow::Connect(WorkflowNode& sourceNode, int sourcePin, WorkflowNode& de // Would be same as `dst.GetSupportedDirection()` because we validated that they are the same above auto direction = src.GetSupportedDirection(); - // TODO check type + // TODO report error to user? + if (src.GetMatchingType() != dst.GetMatchingType()) { + return false; + } using Pt = WorkflowConnection::ConnectionPoint; Pt* srcConnPt; @@ -391,33 +395,37 @@ std::pair<std::unique_ptr<WorkflowNode>&, size_t> Workflow::AllocWorkflowStep() return { mNodes.emplace_back(std::unique_ptr<WorkflowNode>()), id }; } +namespace { +enum class EvaluationStatus { + Unevaluated, + Success, + Failed, +}; +} // namespace + struct WorkflowEvaluationContext::RuntimeNode { - WorkflowNode* Node; + EvaluationStatus Status = EvaluationStatus::Unevaluated; }; struct WorkflowEvaluationContext::RuntimeConnection { - WorkflowConnection* Connection; std::unique_ptr<BaseValue> Value; + + bool IsAvailableValue() const { + return Value != nullptr; + } }; WorkflowEvaluationContext::WorkflowEvaluationContext(Workflow& workflow) : mWorkflow{ &workflow } { - mNodes.resize(workflow.mNodes.size()); - for (size_t i = 0; i < workflow.mNodes.size(); ++i) { - mNodes[i].Node = workflow.mNodes[i].get(); - } - - mConnections.resize(workflow.mConnections.size()); - for (size_t i = 0; i < workflow.mConnections.size(); ++i) { - mConnections[i].Connection = &workflow.mConnections[i]; - } + mRuntimeNodes.resize(workflow.mNodes.size()); + mRuntimeConnections.resize(workflow.mConnections.size()); } BaseValue* WorkflowEvaluationContext::GetConnectionValue(size_t id, bool constant) { if (constant) { return mWorkflow->GetConstantById(id); } else { - return mConnections[id].Value.get(); + return mRuntimeConnections[id].Value.get(); } } @@ -430,7 +438,7 @@ BaseValue* WorkflowEvaluationContext::GetConnectionValue(const WorkflowNode::Inp } void WorkflowEvaluationContext::SetConnectionValue(size_t id, std::unique_ptr<BaseValue> value) { - mConnections[id].Value = std::move(value); + mRuntimeConnections[id].Value = std::move(value); } void WorkflowEvaluationContext::SetConnectionValue(const WorkflowNode::OutputPin& outputPin, std::unique_ptr<BaseValue> value) { @@ -440,17 +448,129 @@ void WorkflowEvaluationContext::SetConnectionValue(const WorkflowNode::OutputPin } void WorkflowEvaluationContext::Run() { - // TODO + std::queue<size_t> candidates; // Stores index to nodes + int evaluatedCount = 0; + int erroredCount = 0; + + // Evaluate all the input nodes first + for (size_t i = 0; i < mRuntimeNodes.size(); ++i) { + if (mWorkflow->mNodes[i]->GetType() == WorkflowNode::InputType) { + candidates.push(i); + } + } + + auto AddOutputsToCandidates = [&](size_t idx) { + auto& node = *mWorkflow->mNodes[idx]; + auto& rNode = mRuntimeNodes[idx]; + for (auto& pin : node.mOutputs) { + if (!pin.IsConnected()) continue; + // TODO support the other variant + if (pin.GetSupportedDirection() != WorkflowConnection::OneToMany) continue; + + auto& rConn = mRuntimeConnections[pin.Connection]; + auto& conn = mWorkflow->mConnections[pin.Connection]; + if (rConn.IsAvailableValue()) { + for (WorkflowConnection::ConnectionPoint& cp : conn.MultiConnections) { + if (rNode.Status != EvaluationStatus::Unevaluated) { + candidates.push(cp.Node); + } + } + } + } + }; + auto FindCandidates = [&]() { + for (size_t i = 0; i < mWorkflow->mNodes.size(); ++i) { + auto& node = mWorkflow->mNodes[i]; + auto& rNode = mRuntimeNodes[i]; + + if (rNode.Status != EvaluationStatus::Unevaluated) { + continue; + } + + for (auto& pin : node->mInputs) { + if (!pin.IsConnected()) continue; + + auto& rConn = mRuntimeConnections[pin.Connection]; + if (!rConn.IsAvailableValue()) { + goto skip; + } + } + + candidates.push(i); + + skip: + continue; + } + }; + + while (true) { + while (!candidates.empty()) { + auto idx = candidates.front(); + auto& node = *mWorkflow->mNodes[idx]; + auto& rNode = mRuntimeNodes[idx]; + candidates.pop(); + + int preEvalErrors = mErrors.size(); + node.Evaluate(*this); + if (preEvalErrors != mErrors.size()) { + erroredCount++; + } else { + evaluatedCount++; + AddOutputsToCandidates(idx); + } + } + + if (evaluatedCount + erroredCount >= mRuntimeNodes.size()) { + break; + } + + // Candidates empty, but there are still possibly-evaluable nodes + FindCandidates(); + } + + for (size_t i = 0; i < mRuntimeNodes.size(); ++i) { + if (mWorkflow->mNodes[i]->GetType() == WorkflowNode::OutputType) { + // TODO + } + } } void WorkflowEvaluationContext::ReportError(std::string message, const WorkflowNode& node, int pinId, bool inputPin) { + mErrors.push_back(WorkflowEvaluationError{ + .Message = std::move(message), + .NodeId = node.GetId(), + .PinId = pinId, + .PinType = inputPin ? WorkflowEvaluationError::InputPin : WorkflowEvaluationError::OutputPin, + .Type = WorkflowEvaluationError::Error, + }); } void WorkflowEvaluationContext::ReportError(std::string message, const WorkflowNode& node) { + mErrors.push_back(WorkflowEvaluationError{ + .Message = std::move(message), + .NodeId = node.GetId(), + .PinId = -1, + .PinType = WorkflowEvaluationError::NoPin, + .Type = WorkflowEvaluationError::Error, + }); } void WorkflowEvaluationContext::ReportWarning(std::string message, const WorkflowNode& node, int pinId, bool inputPin) { + mErrors.push_back(WorkflowEvaluationError{ + .Message = std::move(message), + .NodeId = node.GetId(), + .PinId = pinId, + .PinType = inputPin ? WorkflowEvaluationError::InputPin : WorkflowEvaluationError::OutputPin, + .Type = WorkflowEvaluationError::Warning, + }); } void WorkflowEvaluationContext::ReportWarning(std::string message, const WorkflowNode& node) { + mErrors.push_back(WorkflowEvaluationError{ + .Message = std::move(message), + .NodeId = node.GetId(), + .PinId = -1, + .PinType = WorkflowEvaluationError::NoPin, + .Type = WorkflowEvaluationError::Warning, + }); } diff --git a/core/src/Model/Workflow.hpp b/core/src/Model/Workflow.hpp index 62dd9ca..f1459ba 100644 --- a/core/src/Model/Workflow.hpp +++ b/core/src/Model/Workflow.hpp @@ -90,7 +90,6 @@ protected: }; friend class Workflow; - friend class WorkflowEvaluationError; friend class WorkflowEvaluationContext; Workflow* mWorkflow; @@ -189,8 +188,8 @@ private: struct RuntimeConnection; Workflow* mWorkflow; - std::vector<RuntimeNode> mNodes; - std::vector<RuntimeConnection> mConnections; + std::vector<RuntimeNode> mRuntimeNodes; + std::vector<RuntimeConnection> mRuntimeConnections; std::vector<WorkflowEvaluationError> mErrors; std::vector<WorkflowEvaluationError> mWarnings; diff --git a/core/src/Model/WorkflowNodes.cpp b/core/src/Model/WorkflowNodes.cpp index 14f72cb..f58c8bb 100644 --- a/core/src/Model/WorkflowNodes.cpp +++ b/core/src/Model/WorkflowNodes.cpp @@ -82,7 +82,7 @@ NumericExpressionNode::NumericExpressionNode() : WorkflowNode(TransformType, KD_NumericExpression) { } -struct FormatTextNode::Argument { +struct TextFormatterNode::Argument { ArgumentType ArgumentType; int PinIdx; @@ -108,7 +108,7 @@ struct FormatTextNode::Argument { } }; -BaseValue::Kind FormatTextNode::ArgumentTypeToValueKind(FormatTextNode::ArgumentType arg) { +BaseValue::Kind TextFormatterNode::ArgumentTypeToValueKind(TextFormatterNode::ArgumentType arg) { switch (arg) { case NumericArgument: return BaseValue::KD_Numeric; case TextArgument: return BaseValue::KD_Text; @@ -117,23 +117,23 @@ BaseValue::Kind FormatTextNode::ArgumentTypeToValueKind(FormatTextNode::Argument } } -bool FormatTextNode::IsInstance(const WorkflowNode* node) { +bool TextFormatterNode::IsInstance(const WorkflowNode* node) { return node->GetKind() == KD_TextFormatting; } -FormatTextNode::FormatTextNode() +TextFormatterNode::TextFormatterNode() : WorkflowNode(TransformType, KD_TextFormatting) { } -int FormatTextNode::GetElementCount() const { +int TextFormatterNode::GetElementCount() const { return mElements.size(); } -const FormatTextNode::Element& FormatTextNode::GetElement(int idx) const { +const TextFormatterNode::Element& TextFormatterNode::GetElement(int idx) const { return mElements[idx]; } -void FormatTextNode::SetElement(int idx, std::string text) { +void TextFormatterNode::SetElement(int idx, std::string text) { assert(idx >= 0 && idx < mElements.size()); std::visit( @@ -147,7 +147,7 @@ void FormatTextNode::SetElement(int idx, std::string text) { mElements[idx] = std::move(text); } -void FormatTextNode::SetElement(int idx, ArgumentType argument) { +void TextFormatterNode::SetElement(int idx, ArgumentType argument) { assert(idx >= 0 && idx < mElements.size()); std::visit( @@ -179,7 +179,7 @@ void FormatTextNode::SetElement(int idx, ArgumentType argument) { mElements[idx]); } -void FormatTextNode::InsertElement(int idx, std::string text) { +void TextFormatterNode::InsertElement(int idx, std::string text) { assert(idx >= 0); if (idx >= mElements.size()) AppendElement(std::move(text)); @@ -187,7 +187,7 @@ void FormatTextNode::InsertElement(int idx, std::string text) { mElements.insert(mElements.begin() + idx, std::move(text)); } -void FormatTextNode::InsertElement(int idx, ArgumentType argument) { +void TextFormatterNode::InsertElement(int idx, ArgumentType argument) { assert(idx >= 0); if (idx >= mElements.size()) AppendElement(argument); @@ -206,12 +206,12 @@ void FormatTextNode::InsertElement(int idx, ArgumentType argument) { }); } -void FormatTextNode::AppendElement(std::string text) { +void TextFormatterNode::AppendElement(std::string text) { mMinOutputChars += text.size(); mElements.push_back(std::move(text)); } -void FormatTextNode::AppendElement(ArgumentType argument) { +void TextFormatterNode::AppendElement(ArgumentType argument) { int pinIdx = mInputs.size(); // Create pin mInputs.push_back(InputPin{}); @@ -223,7 +223,7 @@ void FormatTextNode::AppendElement(ArgumentType argument) { }); } -void FormatTextNode::RemoveElement(int idx) { +void TextFormatterNode::RemoveElement(int idx) { assert(idx >= 0 && idx < mElements.size()); PreRemoveElement(idx); @@ -233,7 +233,7 @@ void FormatTextNode::RemoveElement(int idx) { mElements.erase(mElements.begin() + idx); } -void FormatTextNode::Evaluate(WorkflowEvaluationContext& ctx) { +void TextFormatterNode::Evaluate(WorkflowEvaluationContext& ctx) { std::string result; result.reserve((size_t)(mMinOutputChars * 1.5f)); @@ -274,7 +274,7 @@ void FormatTextNode::Evaluate(WorkflowEvaluationContext& ctx) { } } -void FormatTextNode::PreRemoveElement(int idx) { +void TextFormatterNode::PreRemoveElement(int idx) { auto& elm = mElements[idx]; if (auto arg = std::get_if<Argument>(&elm)) { RemoveInputPin(arg->PinIdx); diff --git a/core/src/Model/WorkflowNodes.hpp b/core/src/Model/WorkflowNodes.hpp index e7d4bfc..677158c 100644 --- a/core/src/Model/WorkflowNodes.hpp +++ b/core/src/Model/WorkflowNodes.hpp @@ -39,7 +39,7 @@ public: // TODO }; -class FormatTextNode : public WorkflowNode { +class TextFormatterNode : public WorkflowNode { public: enum ArgumentType { NumericArgument, @@ -57,7 +57,7 @@ private: public: static BaseValue::Kind ArgumentTypeToValueKind(ArgumentType arg); static bool IsInstance(const WorkflowNode* node); - FormatTextNode(); + TextFormatterNode(); int GetElementCount() const; const Element& GetElement(int idx) const; diff --git a/core/src/Model/fwd.hpp b/core/src/Model/fwd.hpp index a7df889..5a75394 100644 --- a/core/src/Model/fwd.hpp +++ b/core/src/Model/fwd.hpp @@ -40,7 +40,7 @@ class WorkflowEvaluationContext; // WorkflowSteps.hpp class NumericOperationNode; class NumericExpressionNode; -class FormatTextNode; +class TextFormatterNode; class DocumentTemplateExpansionNode; class FormInputNode; class DatabaseRowsInputNode; diff --git a/core/src/UI/UI_Workflows.cpp b/core/src/UI/UI_Workflows.cpp index 2b0368e..101fa7b 100644 --- a/core/src/UI/UI_Workflows.cpp +++ b/core/src/UI/UI_Workflows.cpp @@ -1,8 +1,78 @@ #include "UI.hpp" +#include "Model/Workflow.hpp" +#include "Model/WorkflowNodes.hpp" #include "UI/Localization.hpp" #include <imgui.h> +#include <memory> +#include <vector> + +namespace { +class WorkflowCreationMenu { +private: + using WorkflowNodeConstructor = std::unique_ptr<WorkflowNode> (*)(); + + struct Candidate { + WorkflowNodeConstructor Constructor; + }; + + std::vector<Candidate> mCandidates; + +public: + WorkflowCreationMenu() { + SetupCandidates(); + } + +private: + void SetupCandidates() { + mCandidates.push_back(Candidate{ + .Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<NumericOperationNode>(NumericOperationNode::Addition); }, + }); + + mCandidates.push_back(Candidate{ + .Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<NumericOperationNode>(NumericOperationNode::Subtraction); }, + }); + + mCandidates.push_back(Candidate{ + .Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<NumericOperationNode>(NumericOperationNode::Multiplication); }, + }); + + mCandidates.push_back(Candidate{ + .Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<NumericOperationNode>(NumericOperationNode::Division); }, + }); + +// mCandidates.push_back(Candidate{ +// .Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<NumericExpressionNode>(); }, +// }); + + mCandidates.push_back(Candidate{ + .Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<TextFormatterNode>(); }, + }); + +// mCandidates.push_back(Candidate{ +// .Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<DocumentTemplateExpansionNode>(); }, +// }); + +// mCandidates.push_back(Candidate{ +// .Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<FormInputNode>(); }, +// }); + +// mCandidates.push_back(Candidate{ +// .Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<DatabaseRowsInputNode>(); }, +// }); + } +}; + +class WorkflowUI { +private: + Workflow* mWorkflow; + +public: + void Draw() { + } +}; +} // namespace void UI::WorkflowsTab() { // TODO |