📜 ⬆️ ⬇️

A simple generator of the DGML file of the state machine transition graph

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


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 for solving the second task - the implementation of the transition graph generator. But when analyzing the material on this topic, I came across this article , which prompted me to make a decision. So, in this article, the author manually generates a state graph using the Visual Studio tool, namely the visual editor of DGML files (Direct Graph Markup Language), and then, based on the graph obtained, programmatically generates a state machine transition table.

The DGML file (oriented graph file) has an XML representation, the structure of which is perfectly 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 available 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 a graph generator in the 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 XML node Links containing the oriented links of the Link graph is extracted, and the Nodes XML node containing the vertices of the Node graph.

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

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

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

image

In conclusion, I want to note that:


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

Source: https://habr.com/ru/post/269983/


All Articles