diff options
Diffstat (limited to 'core/src/Model/Workflow.cpp')
-rw-r--r-- | core/src/Model/Workflow.cpp | 576 |
1 files changed, 0 insertions, 576 deletions
diff --git a/core/src/Model/Workflow.cpp b/core/src/Model/Workflow.cpp deleted file mode 100644 index 7ca1b9e..0000000 --- a/core/src/Model/Workflow.cpp +++ /dev/null @@ -1,576 +0,0 @@ -#include "Workflow.hpp" - -#include <algorithm> -#include <cassert> -#include <queue> -#include <utility> - -WorkflowConnection::WorkflowConnection() - : MultiConnections{} - , SingleConnection{ WorkflowNode::kInvalidId, -1 } - , ConnectionDirection{ OneToMany } { -} - -bool WorkflowConnection::IsValid() const { - return SingleConnection.Node != WorkflowNode::kInvalidId; -} - -std::span<WorkflowConnection::ConnectionPoint> WorkflowConnection::GetSourcePoints() { - switch (ConnectionDirection) { - case ManyToOne: return MultiConnections; - case OneToMany: return { &SingleConnection, 1 }; - } -} - -std::span<const WorkflowConnection::ConnectionPoint> WorkflowConnection::GetSourcePoints() const { - switch (ConnectionDirection) { - case ManyToOne: return MultiConnections; - case OneToMany: return { &SingleConnection, 1 }; - } -} - -std::span<WorkflowConnection::ConnectionPoint> WorkflowConnection::GetDestinationPoints() { - switch (ConnectionDirection) { - case ManyToOne: return { &SingleConnection, 1 }; - case OneToMany: return MultiConnections; - } -} - -std::span<const WorkflowConnection::ConnectionPoint> WorkflowConnection::GetDestinationPoints() const { - switch (ConnectionDirection) { - case ManyToOne: return { &SingleConnection, 1 }; - case OneToMany: return MultiConnections; - } -} - -bool WorkflowNode::InputPin::IsConstantConnection() const { - return ConnectionToConst && IsConnected(); -} - -bool WorkflowNode::InputPin::IsConnected() const { - return Connection != WorkflowConnection::kInvalidId; -} - -BaseValue::Kind WorkflowNode::InputPin::GetMatchingType() const { - return MatchingType; -} - -WorkflowConnection::Direction WorkflowNode::InputPin::GetSupportedDirection() const { - return AllowsMultipleConnections ? WorkflowConnection::ManyToOne : WorkflowConnection::OneToMany; -} - -bool WorkflowNode::OutputPin::IsConnected() const { - return Connection != WorkflowConnection::kInvalidId; -} - -BaseValue::Kind WorkflowNode::OutputPin::GetMatchingType() const { - return MatchingType; -} - -WorkflowConnection::Direction WorkflowNode::OutputPin::GetSupportedDirection() const { - return AllowsMultipleConnections ? WorkflowConnection::OneToMany : WorkflowConnection::ManyToOne; -} - -WorkflowNode::WorkflowNode(Type type, Kind kind) - : mType{ type } - , mKind{ kind } { -} - -size_t WorkflowNode::GetId() const { - return mId; -} - -WorkflowNode::Type WorkflowNode::GetType() const { - return mType; -} - -WorkflowNode::Kind WorkflowNode::GetKind() const { - return mKind; -} - -void WorkflowNode::ConnectInput(int nodeId, WorkflowNode& output, int outputNodeId) { - mWorkflow->Connect(*this, nodeId, output, outputNodeId); -} - -void WorkflowNode::DisconnectInput(int nodeId) { - mWorkflow->DisconnectByDestination(*this, nodeId); -} - -bool WorkflowNode::IsInputConnected(int nodeId) const { - return mInputs[nodeId].IsConnected(); -} - -void WorkflowNode::ConnectOutput(int nodeId, WorkflowNode& input, int inputNodeId) { - mWorkflow->Connect(input, inputNodeId, *this, nodeId); -} - -void WorkflowNode::DisconnectOutput(int nodeId) { - mWorkflow->DisconnectBySource(*this, nodeId); -} - -bool WorkflowNode::IsOutputConnected(int nodeId) const { - return mOutputs[nodeId].IsConnected(); -} - -WorkflowNode::InputPin& WorkflowNode::InsertInputPin(int atIdx) { - assert(atIdx >= 0 && atIdx < mInputs.size()); - - mInputs.push_back(InputPin{}); - for (int i = (int)mInputs.size() - 1, end = atIdx + 1; i >= end; --i) { - SwapInputPin(i, i + 1); - } - - return mInputs[atIdx]; -} - -void WorkflowNode::RemoveInputPin(int pin) { - DisconnectInput(pin); - for (int i = 0, end = (int)mInputs.size() - 1; i < end; ++i) { - SwapInputPin(i, i + 1); - } - mInputs.resize(mInputs.size() - 1); -} - -void WorkflowNode::SwapInputPin(int a, int b) { - auto& pinA = mInputs[a]; - auto& pinB = mInputs[b]; - - if (mWorkflow) { - // Delay assignment to ConnectionPoint::Pin so that the pinB loop (if it happens to run and looks in the same Connection) doesn't match the point updated in the pinA loop - using Pt = WorkflowConnection::ConnectionPoint; - Pt* pointA = nullptr; - Pt* pointB = nullptr; - - if (pinA.IsConnected() && !pinA.IsConstantConnection()) { - auto pts = mWorkflow->GetConnectionById(pinA.Connection)->GetDestinationPoints(); - auto it = std::find(pts.begin(), pts.end(), Pt{ mId, a }); - pointA = it == pts.end() ? nullptr : &*it; - } - if (pinB.IsConnected() && !pinB.IsConstantConnection()) { - auto pts = mWorkflow->GetConnectionById(pinB.Connection)->GetDestinationPoints(); - auto it = std::find(pts.begin(), pts.end(), Pt{ mId, b }); - pointB = it == pts.end() ? nullptr : &*it; - } - - if (pointA) pointA->Pin = b; - if (pointB) pointB->Pin = a; - } - - std::swap(pinA, pinB); -} - -WorkflowNode::OutputPin& WorkflowNode::InsertOutputPin(int atIdx) { - assert(atIdx >= 0 && atIdx < mOutputs.size()); - - mOutputs.push_back(OutputPin{}); - for (int i = (int)mOutputs.size() - 1, end = atIdx + 1; i >= end; --i) { - SwapOutputPin(i, i + 1); - } - - return mOutputs[atIdx]; -} - -void WorkflowNode::RemoveOutputPin(int pin) { - DisconnectOutput(pin); - for (int i = 0, end = (int)mOutputs.size() - 1; i < end; ++i) { - SwapInputPin(i, i + 1); - } - mOutputs.resize(mOutputs.size() - 1); -} - -void WorkflowNode::SwapOutputPin(int a, int b) { - auto& pinA = mOutputs[a]; - auto& pinB = mOutputs[b]; - - if (mWorkflow) { - using Pt = WorkflowConnection::ConnectionPoint; - Pt* pointA = nullptr; - Pt* pointB = nullptr; - - if (pinA.IsConnected()) { - auto pts = mWorkflow->GetConnectionById(pinA.Connection)->GetSourcePoints(); - auto it = std::find(pts.begin(), pts.end(), Pt{ mId, a }); - pointA = it == pts.end() ? nullptr : &*it; - } - if (pinB.IsConnected()) { - auto pts = mWorkflow->GetConnectionById(pinB.Connection)->GetSourcePoints(); - auto it = std::find(pts.begin(), pts.end(), Pt{ mId, b }); - pointB = it == pts.end() ? nullptr : &*it; - } - - if (pointA) pointA->Pin = b; - if (pointB) pointB->Pin = a; - } - - std::swap(pinA, pinB); -} - -WorkflowConnection* Workflow::GetConnectionById(size_t id) { - return &mConnections[id]; -} - -WorkflowNode* Workflow::GetStepById(size_t id) { - return mNodes[id].get(); -} - -BaseValue* Workflow::GetConstantById(size_t id) { - return mConstants[id].get(); -} - -void Workflow::AddStep(std::unique_ptr<WorkflowNode> step) { - auto [storage, id] = AllocWorkflowStep(); - storage = std::move(step); - storage->OnAttach(*this, id); - storage->mWorkflow = this; - storage->mId = id; -} - -void Workflow::RemoveStep(size_t id) { - auto& step = mNodes[id]; - if (step == nullptr) return; - - step->OnDetach(); - step->mWorkflow = nullptr; - step->mId = WorkflowNode::kInvalidId; -} - -bool Workflow::Connect(WorkflowNode& sourceNode, int sourcePin, WorkflowNode& destinationNode, int destinationPin) { - auto& src = sourceNode.mOutputs[sourcePin]; - auto& dst = destinationNode.mInputs[destinationPin]; - - // Equivalent to `if (o.GetSupportedDirection() != i.GetSupportedDirection()) return false;` - // Cannot connect two pins of different type - if (src.AllowsMultipleConnections == dst.AllowsMultipleConnections) return false; - // Would be same as `dst.GetSupportedDirection()` because we validated that they are the same above - auto direction = src.GetSupportedDirection(); - - // TODO report error to user? - if (src.GetMatchingType() != dst.GetMatchingType()) { - return false; - } - - using Pt = WorkflowConnection::ConnectionPoint; - Pt* srcConnPt; - Pt* dstConnPt; - size_t connId; - - switch (direction) { - case WorkflowConnection::ManyToOne: { - if (dst.IsConnected()) return false; - - WorkflowConnection* conn; - if (src.IsConnected()) { - conn = &mConnections[src.Connection]; - connId = src.Connection; - } else { - auto p = AllocWorkflowConnection(); - conn = &p.first; - connId = p.second; - } - - srcConnPt = &conn->MultiConnections.emplace_back(); - dstConnPt = &conn->SingleConnection; - } break; - - case WorkflowConnection::OneToMany: { - if (src.IsConnected()) return false; - - WorkflowConnection* conn; - if (dst.IsConnected()) { - conn = &mConnections[src.Connection]; - connId = src.Connection; - } else { - auto p = AllocWorkflowConnection(); - conn = &p.first; - connId = p.second; - } - - srcConnPt = &conn->SingleConnection; - dstConnPt = &conn->MultiConnections.emplace_back(); - } break; - } - - srcConnPt->Node = sourceNode.GetId(); - srcConnPt->Pin = sourcePin; - dstConnPt->Node = destinationNode.GetId(); - dstConnPt->Pin = destinationPin; - - src.Connection = connId; - dst.Connection = connId; - return true; -} - -bool Workflow::DisconnectBySource(WorkflowNode& sourceNode, int sourcePin) { - auto& sp = sourceNode.mOutputs[sourcePin]; - if (!sp.IsConnected()) return false; - - auto& conn = mConnections[sp.Connection]; - - using Pt = WorkflowConnection::ConnectionPoint; - switch (sp.GetSupportedDirection()) { - case WorkflowConnection::ManyToOne: { - // Recessive pin, remove ConnectionPoint associated with this pin only - - auto& vec = conn.MultiConnections; - vec.erase(std::remove(vec.begin(), vec.end(), Pt{ sourceNode.GetId(), sourcePin }), vec.end()); - - sp.Connection = WorkflowConnection::kInvalidId; - } break; - - case WorkflowConnection::OneToMany: { - // Dominate pin, removes whole connection - - for (auto& pt : conn.MultiConnections) { - auto& node = *mNodes[pt.Node]; - node.mInputs[pt.Pin].Connection = WorkflowConnection::kInvalidId; - } - sp.Connection = WorkflowConnection::kInvalidId; - - conn = {}; - } break; - } - - return true; -} - -bool Workflow::DisconnectByDestination(WorkflowNode& destinationNode, int destinationPin) { - auto& dp = destinationNode.mInputs[destinationPin]; - if (!dp.IsConnected()) return false; - if (dp.IsConstantConnection()) { - dp.ConnectionToConst = false; - dp.Connection = WorkflowConnection::kInvalidId; - return true; - } - - auto& conn = mConnections[dp.Connection]; - - using Pt = WorkflowConnection::ConnectionPoint; - switch (dp.GetSupportedDirection()) { - case WorkflowConnection::ManyToOne: { - // Dominate pin, removes whole connection - - for (auto& pt : conn.MultiConnections) { - auto& node = *mNodes[pt.Node]; - node.mOutputs[pt.Pin].Connection = WorkflowConnection::kInvalidId; - } - dp.Connection = WorkflowConnection::kInvalidId; - - conn = {}; - } break; - - case WorkflowConnection::OneToMany: { - // Recessive pin, remove ConnectionPoint associated with this pin only - - auto& vec = conn.MultiConnections; - vec.erase(std::remove(vec.begin(), vec.end(), Pt{ destinationNode.GetId(), destinationPin }), vec.end()); - - dp.Connection = WorkflowConnection::kInvalidId; - } break; - } - - return true; -} - -std::pair<WorkflowConnection&, size_t> Workflow::AllocWorkflowConnection() { - for (size_t idx = 0; idx < mConnections.size(); ++idx) { - auto& elm = mConnections[idx]; - if (!elm.IsValid()) { - return { elm, idx }; - } - } - - auto id = mConnections.size(); - return { mConnections.emplace_back(WorkflowConnection{}), id }; -} - -std::pair<std::unique_ptr<WorkflowNode>&, size_t> Workflow::AllocWorkflowStep() { - for (size_t idx = 0; idx < mNodes.size(); ++idx) { - auto& elm = mNodes[idx]; - if (elm == nullptr) { - return { elm, idx }; - } - } - - auto id = mNodes.size(); - return { mNodes.emplace_back(std::unique_ptr<WorkflowNode>()), id }; -} - -namespace { -enum class EvaluationStatus { - Unevaluated, - Success, - Failed, -}; -} // namespace - -struct WorkflowEvaluationContext::RuntimeNode { - EvaluationStatus Status = EvaluationStatus::Unevaluated; -}; - -struct WorkflowEvaluationContext::RuntimeConnection { - std::unique_ptr<BaseValue> 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<BaseValue> value) { - mRuntimeConnections[id].Value = std::move(value); -} - -void WorkflowEvaluationContext::SetConnectionValue(const WorkflowNode::OutputPin& outputPin, std::unique_ptr<BaseValue> value) { - if (outputPin.IsConnected()) { - SetConnectionValue(outputPin.Connection, std::move(value)); - } -} - -void WorkflowEvaluationContext::Run() { - 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, - }); -} |