Exemple #1
0
        protected async Task <StateNodeViewModel> BuildStateNodes(WorkflowDefinition definition, GraphViewModel graph, StateDefinition state, NodeViewModel endNode, NodeViewModel previousNode)
        {
            var           stateNodeGroup = graph.AllClusters.Values.OfType <StateNodeViewModel>().FirstOrDefault(cluster => cluster.State.Name == state.Name);
            NodeViewModel?firstNode, lastNode = null;

            if (stateNodeGroup != null)
            {
                return(stateNodeGroup);
            }
            else
            {
                stateNodeGroup = new StateNodeViewModel(state, state == definition.GetStartState());
                await graph.AddElementAsync(stateNodeGroup);

                switch (state)
                {
                case CallbackStateDefinition callbackState:
                {
                    var actionNodes = await this.BuildActionNodes(graph, callbackState.Action !);

                    lastNode = this.BuildConsumeEventNode(callbackState.Event !);
                    foreach (var actionNode in actionNodes)
                    {
                        await stateNodeGroup.AddChildAsync(actionNode);
                    }
                    await this.BuildEdgeBetween(graph, previousNode, actionNodes.First());

                    await this.BuildEdgeBetween(graph, actionNodes.Last(), lastNode);

                    await stateNodeGroup.AddChildAsync(lastNode);

                    break;
                }

                case EventStateDefinition eventState:
                {
                    firstNode = this.BuildParellelNode();
                    lastNode  = this.BuildParellelNode();
                    await stateNodeGroup.AddChildAsync(firstNode);

                    await this.BuildEdgeBetween(graph, previousNode, firstNode);

                    foreach (var trigger in eventState.Triggers)
                    {
                        var gatewayIn  = this.BuildGatewayNode(eventState.Exclusive ? GatewayNodeType.Xor : GatewayNodeType.And);
                        var gatewayOut = this.BuildGatewayNode(eventState.Exclusive ? GatewayNodeType.Xor : GatewayNodeType.And);
                        await stateNodeGroup.AddChildAsync(gatewayIn);

                        await stateNodeGroup.AddChildAsync(gatewayOut);

                        await this.BuildEdgeBetween(graph, firstNode, gatewayIn);

                        foreach (var eventName in trigger.Events)
                        {
                            var eventNode = this.BuildConsumeEventNode(eventName);
                            await stateNodeGroup.AddChildAsync(eventNode);

                            await this.BuildEdgeBetween(graph, gatewayIn, eventNode);

                            await this.BuildEdgeBetween(graph, eventNode, gatewayOut);
                        }
                        foreach (var action in trigger.Actions)
                        {
                            var actionsNodes = await this.BuildActionNodes(graph, action);

                            foreach (var actionNode in actionsNodes)
                            {
                                await stateNodeGroup.AddChildAsync(actionNode);
                            }
                            await this.BuildEdgeBetween(graph, gatewayOut, actionsNodes.First());

                            await this.BuildEdgeBetween(graph, actionsNodes.Last(), lastNode);
                        }
                    }
                    await stateNodeGroup.AddChildAsync(lastNode);

                    break;
                }

                case ForEachStateDefinition foreachState:
                {
                    firstNode = this.BuildForEachNode(foreachState);
                    lastNode  = this.BuildForEachNode(foreachState);
                    await stateNodeGroup.AddChildAsync(firstNode);

                    await this.BuildEdgeBetween(graph, previousNode, firstNode);

                    foreach (var action in foreachState.Actions)
                    {
                        var actionNodes = await this.BuildActionNodes(graph, action);

                        foreach (var actionNode in actionNodes)
                        {
                            await stateNodeGroup.AddChildAsync(actionNode);
                        }
                        await this.BuildEdgeBetween(graph, firstNode, actionNodes.First());

                        await this.BuildEdgeBetween(graph, actionNodes.Last(), lastNode);
                    }
                    await stateNodeGroup.AddChildAsync(lastNode);

                    break;
                }

                case InjectStateDefinition injectState:
                {
                    lastNode = this.BuildInjectNode(injectState);
                    await stateNodeGroup.AddChildAsync(lastNode);

                    await this.BuildEdgeBetween(graph, previousNode, lastNode);

                    break;
                }

                case OperationStateDefinition operationState:
                {
                    switch (operationState.ActionMode)
                    {
                    case ActionExecutionMode.Parallel:
                        firstNode = this.BuildParellelNode();
                        lastNode  = this.BuildParellelNode();
                        await stateNodeGroup.AddChildAsync(firstNode);

                        await this.BuildEdgeBetween(graph, previousNode, firstNode);

                        foreach (var action in operationState.Actions)
                        {
                            var actionNodes = await this.BuildActionNodes(graph, action);

                            foreach (var actionNode in actionNodes)
                            {
                                await stateNodeGroup.AddChildAsync(actionNode);
                            }
                            await this.BuildEdgeBetween(graph, firstNode, actionNodes.First());

                            await this.BuildEdgeBetween(graph, actionNodes.Last(), lastNode);
                        }
                        await stateNodeGroup.AddChildAsync(lastNode);

                        break;

                    case ActionExecutionMode.Sequential:
                        lastNode = previousNode;
                        foreach (var action in operationState.Actions)
                        {
                            var actionNodes = await this.BuildActionNodes(graph, action);

                            foreach (var actionNode in actionNodes)
                            {
                                await stateNodeGroup.AddChildAsync(actionNode);
                            }
                            await this.BuildEdgeBetween(graph, lastNode, actionNodes.First());

                            lastNode = actionNodes.Last();
                        }
                        break;

                    default:
                        throw new Exception($"The specified action execution mode '{operationState.ActionMode}' is not supported");
                    }
                    break;
                }

                case ParallelStateDefinition parallelState:
                {
                    firstNode = parallelState.CompletionType == ParallelCompletionType.AllOf ? this.BuildParellelNode() : this.BuildGatewayNode(GatewayNodeType.N);
                    lastNode  = parallelState.CompletionType == ParallelCompletionType.AllOf ? this.BuildParellelNode() : this.BuildGatewayNode(GatewayNodeType.N);
                    await stateNodeGroup.AddChildAsync(firstNode);

                    await this.BuildEdgeBetween(graph, previousNode, firstNode);

                    foreach (var branch in parallelState.Branches)
                    {
                        foreach (var action in branch.Actions)
                        {
                            var actionNodes = await this.BuildActionNodes(graph, action);

                            foreach (var actionNode in actionNodes)
                            {
                                await stateNodeGroup.AddChildAsync(actionNode);
                            }
                            await this.BuildEdgeBetween(graph, firstNode, actionNodes.First());

                            await this.BuildEdgeBetween(graph, actionNodes.Last(), lastNode);
                        }
                    }
                    await stateNodeGroup.AddChildAsync(lastNode);

                    break;
                }

                case SleepStateDefinition sleepState:
                {
                    lastNode = this.BuildSleepNode(sleepState);
                    await stateNodeGroup.AddChildAsync(lastNode);

                    await this.BuildEdgeBetween(graph, previousNode, lastNode);

                    break;
                }

                case SwitchStateDefinition switchState:
                {
                    firstNode = this.BuildGatewayNode(GatewayNodeType.Xor);
                    await stateNodeGroup.AddChildAsync(firstNode);

                    await this.BuildEdgeBetween(graph, previousNode, firstNode);

                    switch (switchState.SwitchType)
                    {
                    case SwitchStateType.Data:
                    {
                        foreach (var condition in switchState.DataConditions)
                        {
                            var caseNode = this.BuildDataConditionNode(condition.Name !);                // todo: should be a labeled edge, not a node?
                            await stateNodeGroup.AddChildAsync(caseNode);

                            await this.BuildEdgeBetween(graph, firstNode, caseNode);

                            switch (condition.Type)
                            {
                            case ConditionType.End:
                                await this.BuildEdgeBetween(graph, caseNode, endNode);

                                break;

                            case ConditionType.Transition:
                                var nextStateName = condition.Transition == null ? condition.TransitionToStateName : condition.Transition.NextState;
                                var nextState     = definition.GetState(nextStateName !);
                                if (nextState == null)
                                {
                                    throw new Exception($"Failed to find a state with name '{nextStateName}' in definition '{definition.GetUniqueIdentifier()}");
                                }
                                var nextStateNode = await this.BuildStateNodes(definition, graph, nextState, endNode, caseNode);

                                lastNode = nextStateNode.Children.Values.OfType <NodeViewModel>().Last();
                                break;

                            default:
                                throw new Exception($"The specified condition type '${condition.Type}' is not supported");
                            }
                        }
                        var defaultCaseNode = this.BuildDataConditionNode("default");
                        await stateNodeGroup.AddChildAsync(defaultCaseNode);

                        await this.BuildEdgeBetween(graph, firstNode, defaultCaseNode);

                        if (switchState.DefaultCondition.IsEnd ||
                            switchState.DefaultCondition.End != null)
                        {
                            lastNode = defaultCaseNode;
                            if (!state.IsEnd && state.End == null)
                            {
                                await this.BuildEdgeBetween(graph, lastNode, endNode);
                            }
                        }
                        else if (!string.IsNullOrWhiteSpace(switchState.DefaultCondition.TransitionToStateName) ||
                                 switchState.DefaultCondition.Transition != null)
                        {
                            var nextStateName = switchState.DefaultCondition.Transition == null ? switchState.DefaultCondition.TransitionToStateName : switchState.DefaultCondition.Transition.NextState;
                            var nextState     = definition.GetState(nextStateName !);
                            if (nextState == null)
                            {
                                throw new Exception($"Failed to find a state with name '{nextStateName}' in definition '{definition.GetUniqueIdentifier()}");
                            }
                            var nextStateNode = await this.BuildStateNodes(definition, graph, nextState, endNode, defaultCaseNode);

                            lastNode = nextStateNode.Children.Values.OfType <NodeViewModel>().Last();
                        }
                    }
                    break;

                    case SwitchStateType.Event:
                    {
                        foreach (var condition in switchState.EventConditions)
                        {
                            Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(condition));
                            var caseNode = this.BuildDataConditionNode(condition.Name ?? condition.Event);                 // todo: should be a labeled edge, not a node?
                            await stateNodeGroup.AddChildAsync(caseNode);

                            await this.BuildEdgeBetween(graph, firstNode, caseNode);

                            switch (condition.Type)
                            {
                            case ConditionType.End:
                                await this.BuildEdgeBetween(graph, caseNode, endNode);

                                break;

                            case ConditionType.Transition:
                                var nextStateName = condition.Transition == null ? condition.TransitionToStateName : condition.Transition.NextState;
                                var nextState     = definition.GetState(nextStateName !);
                                if (nextState == null)
                                {
                                    throw new Exception($"Failed to find a state with name '{nextStateName}' in definition '{definition.GetUniqueIdentifier()}");
                                }
                                var nextStateNode = await this.BuildStateNodes(definition, graph, nextState, endNode, caseNode);

                                lastNode = nextStateNode.Children.Values.OfType <NodeViewModel>().Last();
                                break;

                            default:
                                throw new Exception($"The specified condition type '${condition.Type}' is not supported");
                            }
                        }
                        var defaultCaseNode = this.BuildDataConditionNode("default");
                        await stateNodeGroup.AddChildAsync(defaultCaseNode);

                        await this.BuildEdgeBetween(graph, firstNode, defaultCaseNode);

                        if (switchState.DefaultCondition.IsEnd ||
                            switchState.DefaultCondition.End != null)
                        {
                            lastNode = defaultCaseNode;
                            if (!state.IsEnd && state.End == null)
                            {
                                await this.BuildEdgeBetween(graph, lastNode, endNode);
                            }
                        }
                        else if (!string.IsNullOrWhiteSpace(switchState.DefaultCondition.TransitionToStateName) ||
                                 switchState.DefaultCondition.Transition != null)
                        {
                            var nextStateName = switchState.DefaultCondition.Transition == null ? switchState.DefaultCondition.TransitionToStateName : switchState.DefaultCondition.Transition.NextState;
                            var nextState     = definition.GetState(nextStateName !);
                            if (nextState == null)
                            {
                                throw new Exception($"Failed to find a state with name '{nextStateName}' in definition '{definition.GetUniqueIdentifier()}");
                            }
                            var nextStateNode = await this.BuildStateNodes(definition, graph, nextState, endNode, defaultCaseNode);

                            lastNode = nextStateNode.Children.Values.OfType <NodeViewModel>().Last();
                        }
                    }
                    break;

                    default:
                        throw new Exception($"The specified switch state type '{switchState.Type}' is not supported");
                    }
                    break;
                }

                default:
                    throw new Exception($"The specified state type '{state.Type}' is not supported");
                }
            }
            if (lastNode == null)
            {
                throw new Exception($"Unable to define a last node for state '{state.Name}'. Every switch case should provide a last node.");
            }
            if (state.IsEnd ||
                state.End != null)
            {
                //Console.WriteLine($"State '{state.Name}' ends");
                await this.BuildEdgeBetween(graph, lastNode, endNode);

                return(stateNodeGroup);
            }
            if (!string.IsNullOrWhiteSpace(state.TransitionToStateName) ||
                state.Transition != null)
            {
                var nextStateName = state.Transition == null ? state.TransitionToStateName ! : state.Transition !.NextState;
                //Console.WriteLine($"State '{state.Name}' transitions to '{nextStateName}'");
                var nextState = definition.GetState(nextStateName);
                if (nextState == null)
                {
                    throw new Exception($"Failed to find a state with name '{nextStateName}' in definition '{definition.GetUniqueIdentifier()}'");
                }
                var nextStateNode = await this.BuildStateNodes(definition, graph, nextState, endNode, lastNode);

                await this.BuildEdgeBetween(graph, lastNode, nextStateNode.Children.Values.OfType <NodeViewModel>().First());

                return(stateNodeGroup);
            }
            //Console.WriteLine($"No transition for state '{state.Name}'");
            //Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(state));
            return(stateNodeGroup);
        }