Использование Graphviz для построения блок-схем

Мы создаем ПО для разработки и поддержки баз данных Oracle, и статический анализатор PL/SQL является одной из основных фич наших приложений. Кто знаком с Oracle, тот хорошо знает что такое PL/SQL.

Известная поговорка гласит: «Лучше один раз увидеть, чем сто раз услышать». Поэтому мы решили заимпрувить статический анализатор таким образом, чтобы он визуализировал код в виде блок-схем (Flowcharts) и диаграмм вызовов (Call Trees). Хоть и нарисовать блоки и их связи несложно, оптимизировать их расположение на «листе» представлялось задачей, требующей значительных усилий. Чтобы стрелки минимально пересекались и обтекали блоки, блоки объединялись в группы, и диаграмма при этом не превращалась в «кашу», нужно было потратить много сил и времени.

И тогда мы решили поискать готовое решение, дабы не изобретать велосипед. Наше внимание сразу привлек Graphviz – open source решение по визуализации диаграмм. Первые его версии были разработаны компанией AT&T, а теперь он доступен как набор утилит и библиотек, а также в исходниках под лицензией Eclipse Public License (EPL).

Его движок диаграмм использует язык описания графов DOT, который представляет собой текстовое описание структуры графа: вершины, их связи, группы и атрибуты для их визуального оформления.

Описание простейшего графа:

digraph graphname {
  a -> b;
}

Если необходимо добавить узлам атрибуты, например подписи, то необходимо отдельно описать узлы:

digraph graphname {
  a [label="source"]
  b [label="destination"]
  a -> b;
}

Далее покажу на примере простой процедуры на PL/SQL:

CREATE PROCEDURE WRITE_STRING(
    P_MESSAGE_TYPE IN NUMBER
  , P_MESSAGE IN VARCHAR2
)
IS
BEGIN
  IF P_MESSAGE_TYPE >= LOG_LEVEL THEN
    ADD_LOG_RECORD(P_MESSAGE_TYPE, P_MESSAGE);
  END IF;
END WRITE_STRING;

Код понятен, даже если вы не знакомы с синтаксисом PL/SQL.

А теперь опишем этот код на языке DOT. Пояснения снова излишни:

digraph G {
  N0 [label="”]
  N1_COND [label="P_MESSAGE_TYPE \>= LOG_LEVEL ?"];
  N2 [label="ADD_LOG_RECORD(P_MESSAGE_TYPE, P_MESSAGE);"];
  EXIT_LABEL [label="END"];
  
  N0 -> N1_COND
  N1_COND -> N2
  N1_COND -> EXIT_LABEL
  N2 -> EXIT_LABEL 
}

Теперь можно «скормить» этот файл Graphviz, подставив соответственно вместо %PNGFILE% и %DOTFILE% имена выходного (png) и входного (dot) файлов:

dot -v -Tpng –o%PNGFILE% %DOTFILE%

При помощи Graphviz из описания выше получается такая картина:



Наглядно, но весьма аскетично.

Можно сделать диаграмму привлекательнее, добавив атрибуты для определения формы блоков (shape=diamond), подписи стрелок (label="Yes") и цвета color, fontcolor.

Также мы можем объединить в подграф блоки, которые являются исполняемыми. Это реализуется конструкцией subgraph {}, внутри которой мы можем перечислить имена блоков, включенных в подграф, и указать атрибуты для визуального оформления (цвет рамки).

digraph G {
  comment="PL/SQL flowchart generated by ClearSQL";
  node [fontname="Arial", fontsize=8, height=.2, width=.25, color="#000000", fontcolor="#000000"];
  edge [fontname="Arial", fontsize=8, arrowsize=.8, color="#000000", fontcolor="#000000"];
  
  N0 [shape=plaintext, label="", style=invis];
  N1_COND [shape=diamond, conditional=true, label="P_MESSAGE_TYPE \>= LOG_LEVEL ?"];
  N2 [shape=box, label="ADD_LOG_RECORD(P_MESSAGE_TYPE, P_MESSAGE);"];
  EXIT_LABEL [shape=Msquare, label="END"];
  
  N0 -> N1_COND [color=darkgoldenrod];
  N1_COND -> N2 [label="  Yes " color="#228b22" fontcolor="#228b22"];
  N1_COND -> EXIT_LABEL [label="  No " color="#ff7e40" fontcolor="#ff7e40"];
  N2 -> EXIT_LABEL  
  
  subgraph "cluster_#1" {
    color="#0000ff"; 
    N1_COND; N2
  }  
}

Получается вполне симпатичная блок-схема, которую и людям показать не стыдно.



Чтобы код могли читать не только программисты, но и «простые смертные», мы придумали такую фишку: приделали распознавание специальных тегов в комментариях, которые бы разработчик писал как псевдокод – язык описания алгоритмов. Это помогло увеличить ценность блок-схемы, которая теперь может отображать бизнес-логику, а не только визуализировать листинг.

CREATE PROCEDURE WRITE_STRING(
      P_MESSAGE_TYPE IN NUMBER
    , P_MESSAGE IN VARCHAR2
)
IS
BEGIN
  --## Уровень данного сообщения по отношению к порогу журналирования
  --##@true Выше
  --##@false Ниже
  IF P_MESSAGE_TYPE >= LOG_LEVEL THEN
    --## Добавляем запись в журнал
    ADD_LOG_RECORD(P_MESSAGE_TYPE, P_MESSAGE);
  END IF;
END WRITE_STRING;

Вуаля! Graphviz дает возможность делать потрясающие визуализации с минимальными затратами.


Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 13

    0
    Graphviz еще используется в https://symfony.com/doc/current/components/workflow.html
    Удобно и наглядно получаются графики возможных переходов состояний у объекта
    image
      0

      Я в своем проекте сппр так же использую Graphviz. Это позвояет визуализировать логическую структуру. Так же следует упомянуть о других возможностях. Это использование svg формата. Что позволяет визуализировть в браузерах (более удобно) и возможностях гипер-ссылок. Что позволяет сделать отображение еще и интерактивным.

        0
        решение интересное, правда риски всё теже — разъедется содержимое псевдокода с реализацией при активной разработке
          0
          Согласен. Актуальность комментариев в коде тоже надо поддерживать.
            0

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

              0
              Мне кажется это не совсем так. Есть два типа блока, с помощью которых можно выводить данные в виде таблицы: shape=record|Mrecord. Отличие второго от первого — скругленные наружные углы. Больше ни в каких типах блоков сделать столбцы/строки нельзя.
            0
            Я для этого сделал так чтобы при сборке диаграммы автоматически актуализировались. Фактически, они каждый раз пересоздаются по актуальному коду.
            +1
            Часто блок-схемы (Activity Diagram) составляю с помощью plantuml, который для некоторых случаев использует graphviz под капотом.

            У plantuml, по-моему, язык попроще.

            Такой текст:

            @startuml
            start
            
            if (Graphviz installed?) then (yes)
              :process all\ndiagrams;
            else (no)
              :process only
              __sequence__ and __activity__ diagrams;
            endif
            
            stop
            
            @enduml


            Отрисует такую диаграмму:

            image

            Так же есть плагины для использования в IDE от Jetbrains.

            Еще пара-тройка примеров
            image
            image
            image
            image
            image
              0
              Используем Graphviz + PlantUML для авто генерации диаграмм классов и для составления документации (Sequence Diagram). Очень удобно для составления документации по проекту.
                0
                Действительно рисует схемы. Если не секрет, поделитесь параметрами для увеличения разрешения изображения. Что-то векторных форматов в коммандной строке не обнаружил.
                  0
                  Он рисует в векторе. Просто потом вектор сохраняется в растровый формат (это уже зависит от настроек).
                    0
                    Список поддерживаемых форматов можно посмотреть здесь: http://www.graphviz.org/content/output-formats
                    Как минимум есть svg, ps, eps и pdf. Я обычно для самодостаточных схем использую pdf.
                    Для явного задания размера и dpi есть -Gsize и -Gdpi.
                    Обратите внимание, что для точного задания размера нужно добавить восклицательный знак в конце: -Gsize=<width>,<height>!.
                      0
                      Спасибо, очень пригодится!

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                  Самое читаемое