Простой генератор DGML-файла графа переходов машины состояний

от автора

Допустим, есть проект WPF/MVVM, в котором необходимо реализовать шаблон State Machine, позволяющий управлять поведением объекта (в данном случае, ViewModel) в зависимости от того состояния, в котором он находится. При этом необходимо получить простую реализацию этого шаблона без использования Windows Workflow Foundation, которая включала бы в себя классы состояний, класс реализующий логику переходов и таблицу переходов. И наряду с вопросами реализации этого шаблона стоит задача реализации инструмента, автоматизирующего процесс построения диаграммы состояний на основе таблицы переходов. При этом граф, построенный с помощью этого инструмента, должен отвечать следующим требованиям:

  • граф должен обладать понятной и упорядоченной визуальной структурой (ручное упорядочивание вершин и связей графа должно быть сведено к минимуму);
  • файл графа должен быть включен в проект и, соответственно, в систему контроля версий;
  • вершина графа должны обладать кликабельной ссылкой на файл, в котором реализовано состояние;
  • должна быть реализована возможность задавать стили к вершинам графа.


Так, если про реализацию паттерна машины состояний в контексте проекта WPF/MVVM есть достаточно материала, то для решения второй задачи – реализации генератора графа переходов – очевидного решения не нашлось. Но при анализе материала на эту тему я наткнулся на эту статью, которая меня и натолкнула на решение. Так, в этой статье автор вручную формирует граф состояний с помощью инструмента Visual Studio, а именно визуального редактора DGML-файлов (Direct Graph Markup Language), и далее, на основе полученного графа, программно формирует таблицу переходов машины состояний.

DGML-файл (файл ориентированного графа) имеет XML представление, структура которого отлично описана в MSDN. Так, программно редактируя XML представление можно изменить визуальное представление графа. Таким образом, был выбран инструмент визуализации графа, осталось реализовать генератор, который на основе имеющейся таблицы переходов формировал бы XML представление DGML-файла.

Так было принято решение добавить DGML-файл в решение проекта и реализовать генератор графа в тестовом методе:

        [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);                         } 

В начале метода на основе относительного пути к DGML-файлу проекта загружается XML-документ, из которого извлекаются XML-узел Links, содержащий ориентированные связи графа Link, и XML-узел Nodes, содержащий вершины графа Node.

Далее, на основе коллекции состояний clientStateMachine.StatesCollection формируются вершины графа, у которых устанавливаются ссылки на файлы состояний и цвет фона.

Затем, на основе каждого перехода из таблицы переходов clientStateMachine.Transitions, имеющего начальное InitialState и конечное FinalState состояния, формируется направленное ребро графа путем добавления соответствующих атрибутов Source и Target в XML-элемент Link.

Результат выполнения этого тестового метода представлен на рисунке ниже.

image

В заключение, хочу отметить, что:

  • наглядная структура графа, без наложения или пересечения вершин и связей, была получена автоматически с помощью конструктора макета графа, что является отличным преимуществом данного инструмента Visual Studio;
  • перейти по ссылке к файлу состояния можно из контекстного меню вершины графа;
  • представленный генератор можно легко адаптировать под любую машину состояний, имеющую таблицу переходов.

Таким образом, представлена простая, но эффективная реализация генератора ориентированного графа в тестовом методе, выполнение которого позволяет получить актуальную версию диаграммы состояний.

ссылка на оригинал статьи http://habrahabr.ru/post/269983/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *