#include "Evaluation.hpp" #include namespace { enum class EvaluationStatus { Unevaluated, Success, Failed, }; } // namespace struct WorkflowEvaluationContext::RuntimeNode { EvaluationStatus Status = EvaluationStatus::Unevaluated; }; struct WorkflowEvaluationContext::RuntimeConnection { std::unique_ptr Value; bool IsAvailableValue() const { return Value != nullptr; } }; WorkflowEvaluationContext::WorkflowEvaluationContext(Workflow& workflow) : mWorkflow{ &workflow } { 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 mRuntimeConnections[id].Value.get(); } } BaseValue* WorkflowEvaluationContext::GetConnectionValue(const WorkflowNode::InputPin& inputPin) { if (inputPin.IsConnected()) { return GetConnectionValue(inputPin.Connection, inputPin.IsConstantConnection()); } else { return nullptr; } } void WorkflowEvaluationContext::SetConnectionValue(size_t id, std::unique_ptr value) { mRuntimeConnections[id].Value = std::move(value); } void WorkflowEvaluationContext::SetConnectionValue(const WorkflowNode::OutputPin& outputPin, std::unique_ptr value) { if (outputPin.IsConnected()) { SetConnectionValue(outputPin.Connection, std::move(value)); } } void WorkflowEvaluationContext::Run() { std::queue 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, }); }