Simple State Machine Transition Graph DGML File Generator

Suppose there is a WPF / MVVM project in which you need to implement the State Machine template, which allows you to control the behavior of the object (in this case, ViewModel) depending on the state in which it is located. In this case, you need to get a simple implementation of this template without using the Windows Workflow Foundation, which would include state classes, a class that implements the transition logic and the transition table. And along with the implementation issues of this template, the task is to implement a tool that automates the process of constructing a state diagram based on a transition table. In this case, the graph constructed using this tool must meet the following requirements:

  • the graph should have a clear and ordered visual structure (manual ordering of the vertices and links of the graph should be minimized);
  • the graph file should be included in the project and, accordingly, in the version control system;
  • the top of the graph must have a clickable link to the file in which the state is implemented;
  • the ability to set styles to the vertices of the graph should be implemented.

So, if there is enough material about the implementation of the state machine pattern in the context of the WPF / MVVM project, then there was no obvious solution to the second task - the implementation of the transition graph generator. But when analyzing material on this topic, I came across this article , which prompted me to a solution. So, in this article, the author manually creates a state graph using the Visual Studio tool, namely, the visual editor of DGML files (Direct Graph Markup Language), and then, based on the received graph, programmatically forms a state machine transition table.

The DGML file (oriented graph file) has an XML representation, the structure of which is well described in MSDN. So, programmatically editing the XML representation, you can change the visual representation of the graph. Thus, the graph visualization tool was chosen, it remains to implement a generator that, based on the existing transition table, would form an XML representation of the DGML file.

So it was decided to add a DGML file to the project solution and implement the graph generator in a test method:

        [TestMethod]
        public void ClientStateMachineTest()
        {
            // Экземпляр машины состояний ClientStateMachine
            var clientStateMachine = new ClientStateMachine();
            var xmlDoc = new XmlDocument();
            // Относительный путь до DGML-файла, включенного в решение проекта
            const string fileDgml = @"..\..\SM\Test\ClientStateMachineGraph.dgml";
            xmlDoc.Load(fileDgml);
            var nodeLinks = xmlDoc.SelectSingleNode("/*[local-name()='DirectedGraph']/*[local-name()='Links']");
            var nodes = xmlDoc.SelectSingleNode("/*[local-name()='DirectedGraph']/*[local-name()='Nodes']");
            if (nodes != null)
            {
                nodes.RemoveAll();
                foreach (var state in clientStateMachine.StatesCollection)
                {
                    var newNode = xmlDoc.CreateNode(XmlNodeType.Element, "Node", "http://schemas.microsoft.com/vs/2009/dgml");
                    var id = xmlDoc.CreateAttribute("Id");
                    id.Value = state.GetType().Name;
                    var reference = xmlDoc.CreateAttribute("Reference");
                    reference.Value = string.Format(@"..\..\SM\States\{0}.cs", state.GetType().Name);
                    var background = xmlDoc.CreateAttribute("Background");
                    background.Value = state.Background.Name;
                    if (newNode.Attributes != null)
                    {
                        newNode.Attributes.Append(id);
                        newNode.Attributes.Append(background);
                        newNode.Attributes.Append(reference);
                    }
                    nodes.AppendChild(newNode);
                }
            }
            if (nodeLinks != null)
            {
                nodeLinks.RemoveAll();
                foreach (var tr in clientStateMachine.Transitions)
                {
                    var newLink = xmlDoc.CreateNode(XmlNodeType.Element, "Link", "http://schemas.microsoft.com/vs/2009/dgml");
                    var source = xmlDoc.CreateAttribute("Source");
                    source.Value = (tr.Value.InitialState).GetType().Name;
                    var target = xmlDoc.CreateAttribute("Target");
                    target.Value = tr.Value.FinalState.GetType().Name;
                    if (newLink.Attributes != null)
                    {
                        newLink.Attributes.Append(source);
                        newLink.Attributes.Append(target);
                    }
                    nodeLinks.AppendChild(newLink);
                }
            }
            xmlDoc.Save(fileDgml);                
        }

At the beginning of the method, based on the relative path to the project DGML file, an XML document is loaded from which the Links XML node containing the oriented links of the Link graph and the Nodes XML node containing the nodes of the Node graph are extracted.

Further, based on the clientStateMachine.StatesCollection state collection, the vertices of the graph are formed, which establish links to state files and the background color.

Then, based on each transition from the clientStateMachine.Transitions transition table, which has the initial InitialState and final FinalState states, a directed graph edge is formed by adding the corresponding Source and Target attributes to the Link XML element.

The result of this test method is presented in the figure below.

image

In conclusion, I want to note that:

  • the visual structure of the graph, without overlapping or intersecting vertices and links, was obtained automatically using the graph layout designer, which is an excellent advantage of this Visual Studio tool;
  • You can follow the link to the status file from the context menu of the graph vertex;
  • The presented generator can be easily adapted to any state machine that has a conversion table.

Thus, a simple but effective implementation of the directed graph generator in a test method is presented, the implementation of which allows you to get the current version of the state diagram.

Also popular now: