Графическое представление программы плюс результат трансляции
Графическое представление программы плюс результат трансляции

Краткая история разработки

Представление программного кода в виде конечного автомата подкупает надежностью и предсказуемостью результатов. Действительно, Finite-state machine всегда находится только в одном состоянии, что значительно облегчает поиск ошибок. Но вот ее код весьма объемен и его невозможно сходу охватить взглядом. Поэтому фреймворки, помогающие создавать только код, не пользуются широкой популярностью. Другое дело, графическое представление конечного автомата, например, Stateflow в Matlab. Но Matlab это дорого и тяжеловесно, тем более что упор там сделан на классические автоматы, а программирование - скорее приятное дополнение. Куда больше понравился продукт от Quantum Leaps. Это именно то, что хотелось бы видеть у себя в лаборатории! Но конские цены полностью отбивают всё желание.

Выход нашелся: работы Анатолия Шалыто - под общим названием "Автоматное программирование". Очень много полезной теории, но наибольший интерес представляла его ранняя разработка, а именно программа трансляции рисунка Microsoft Visio в код на языке Си. Это было именно то, что нужно. Увы Анатолий Абрамович углубился в академическую деятельность (всецело одобряю), и полезная программа оказалась заброшена.

Пришлось повторять уже сделанное, используя концепцию Автоматного программирования в качестве стартовой точки. В результате получился программный комплекс V2S, в котором конечные автоматы представлены графически, а упор сделан именно на программирование. За время эксплуатации, V2S позволила разработать эффективный код в ряде коммерческих проектов. Из самых крупных - система контроля высева пропашных культур "Топаз" на базе микроконтроллера STM32F412.

Фрагмент конечного автомата системы "Топаз"
Фрагмент  конечного автомата системы "Топаз"
Фрагмент конечного автомата системы "Топаз"

Немного о требованиях V2S.

Самым быстрым путем реализации графической подсистемы V2S оказалось использование Microsoft Visio 2016, разработчики которого изрядно поработали над средствами автоматизации графических примитивов. Плюс, встроенный в документ VBA (Visual Basic for Aplication), на котором в итоге и написан V2S. Это достоинство (полная открытость кода) и недостаток одновременно: например, необходимо разрешение на запуск макросов в среде Microsoft Visio, что для некоторых представляет проблему в плане личностного неприятия.

На GitHub можно скачать свежую версию программы V2S. Это два файла: Auto.vssx, содержит прототипы графических объектов и файл графического документа Microsoft Visio aClockArduino.vsdm (имя не принципиально). Документ можно переименовать по своему вкусу, но нужно учитывать, что после трансляции имя файла принимается в качестве имени глобальной переменной. То есть, только латиница. Оба скачанных файла сохраняем в папку вашего проекта Arduino.
Начинаем графическое программирование открытием документа Microsoft Visio. На вопрос о разрешении запуска макросов следует ответить положительно, в дальнейшем этот документ будет занесен в список доверенных, а запрос безопасности перестанет появляться. Но если запрос не появился вообще, возможно в настройках центра безопасности макросы отключены "без уведомления". Тогда следует восстановить настройки по умолчанию.

Настройка центра управления безопасностью Visio по умолчанию
Настройка центра управления безопасностью Visio по умолчанию
Настройка центра управления безопасностью Visio по умолчанию

Автомат измерения температуры (пример).

Конечный автомат измерения температуры в графической форме
Конечный автомат измерения температуры в графической форме

Графически, конечный автомат измерения температуры DS18x20 представляет собой прямоугольник. Три текстовых поля по углам: Temperature - имя автомата (латиница), внизу пояснения к автомату (будут размещены в комментариях) и справа вверху - поле изменения цвета. Цвет внутренних элементов измениться только после трансляции (если Вы считаете, что удобнее менять сразу, напишите отзыв - в следующих версиях пожелания будут учтены).

Автомат имеет два состояния (иначе называемых вершины), с номерами 10 и 20 соответственно. Каждое состояние включает три текстовых поля, сверху вниз: "Имя автомата" (любой текст, будет размещен в комментариях Си), "Код In" (запускается при входе в состояние) и "Код Out" (запускается при окончательном выходе из состояния). В полях In-Out может быть размещен программный код на языке Си, с учетом экономии места на рисунке, предпочтение обычно отдается вызову функций - так получается нагляднее. Но это правило - не догма, в качестве примера: автомат отправки сообщение через модем (из проекта Топаз) содержит визуально крупные куски кода, но это только улучшило понимание программы.

Фрагмент автомата отправки сообщений через модем
Фрагмент автомата отправки сообщений через модем
Фрагмент автомата отправки сообщений через модем

Вершины объединены переходами, то есть дугами со стрелками на конце. На желтом текстовом поле, поверх линии, записано условие перехода. Это любые комбинации переменных и функций, синтаксис которых позволяет записать их внутрь if (condition) языка Си, а также макрокоманды V2S. Например, для автомата измерения температуры была применена макрокоманда задержки перехода WAIT().

Трансляция из графики в код.

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

Меню запуска транслятора
Меню запуска транслятора
Меню запуска транслятора

Спустя некоторое время, успешность трансляции подтверждает всплывающее сообщение.

Всплывающий бокс результатов трансляции
Всплывающий бокс результатов трансляции
Всплывающий бокс результатов трансляции

В папке размещение проекта появятся два новых файла: aClockArduino.cpp и aClockArduino.h (имя соответствует графическому документу Visio). IDE Arduino или Visual Studio Code (последний очень рекомендую для реальной работы) автоматически подтянут эти файлы в проект. И будут делать так в дальнейшем, при каждом запуске трансляции.

Откроем файлы на просмотр, прямо из IDE. Как и ожидалось, внутри длинный код. На самом деле, открывать их приходится только при отладке, если же все получается гладко, достаточно графического представления. Тем не менее, большинство важных операций результирующего Си кода снабжено комментариями на выбранном вами языке.

Код Temperature()
void Temperature(void) //  процедуры конечного автомата Автомат измерения температуры  DS18x20
{
   if ( SW_Temperature.uNewState != SW_Temperature.uCurState )  //   Если в ходе предыдущего шага сделан был запрос на изменение состояния конечного автомата  
      {
         in_Temperature();  //  Обработка ВХОДОВ в состояния для конечного автомата Автомат измерения температуры  DS18x20

         SW_Temperature.uCurState = SW_Temperature.uNewState;  //  Изменить состояние конечного автомата  Автомат измерения температуры  DS18x20
      }
   conn_Temperature();  //   Обработка процедуры ПЕРЕХОДА между Состояними в конечном автомате Автомат измерения температуры  DS18x20
}

В теории, код обслуживания вершины состоит из трех секций: обработки процедуры входа в состояние, обработки переходов и обработки выхода из состояния. Секция обработки входа представлена функцией in_Temperature(), обработки переходов - функцией conn_Temperature(), а последней секции out_Temperature() нет вовсе, так как поля "Код Out" состояний автомата не заполнены.

Функция in_Temperature() в основе классическая switch-case. Номера при Case соответствуют номерам вершин исходного рисунка (именно поэтому номера должны быть уникальны в пределах автомата). В код, помимо пользовательских функций, внесенных в поля "Код In", транслятор добавил вызов функций управления временем задержки WAIT.

Функция обработки входа в вершину
void in_Temperature(void)  //  Обработка ВХОДОВ в состояния для конечного автомата Автомат измерения температуры  DS18x20
{
  switch (SW_Temperature.uNewState) //  IN Case
     {
     case 10: //  состояние Старт измерения DS18x20
          DS18x20_start();
          set_SW_Temperature_WaitTime(1,1000); //   Задать время для WAIT(1000)
          set_SW_Temperature_WaitCount(1,-1); //  Количество повторений неограниченно для WAIT(1000)
     break;
     case 20: //  состояние Измерение температуры
          DS18x20_read();
          set_SW_Temperature_WaitTime(1,6000); //   Задать время для WAIT(6000)
          set_SW_Temperature_WaitCount(1,-1); //  Количество повторений неограниченно для WAIT(6000)
     break;
     }
}

Больший интерес представляет функция переходов. В switch-case выполняется проверка функции обнуление таймера is_SW_Temperature_Wait(), в которую превратилась макрокоманда WAIT, от истинности этого события зависит появление запроса на переход в новое состояние.

void conn_Temperature(void)  //  Обработка процедуры ПЕРЕХОДА между Состояниями в конечном автомате Автомат измерения температуры  DS18x20
{
 
 switch (SW_Temperature.uCurState)
  {
  case 10: //  состояние Старт измерения DS18x20
     if (is_SW_Temperature_Wait(1))  // 1 Переход по условию:  WAIT(1000)
        {
         SW_Temperature.uNewState = 20; //  Перейти в новое состояние Измерение температуры
        }
  break;
  case 20: //  состояние Измерение температуры
     if (is_SW_Temperature_Wait(1))  // 1 Переход по условию:  WAIT(6000)
        {
         SW_Temperature.uNewState = 10; //  Перейти в новое состояние Старт измерения DS18x20
        }
  break;
  }
}

Конечный автомат измерения температуры, вместе с прочими автоматами, изображенными в документе Visio, размещены в функции aClockArduino(), названной по имени графического файла проекта.

void aClockArduino(void) //  Отработка шага вычислений для всех конечных автоматов проекта  
{
    if (!SW_aClockArduino.uStart)  //  Проверка первого запуска конечных автоматов проекта  
    {
        aClockArduino_init(); //  Инициализация всех конечных автоматов проекта  
        SW_aClockArduino.uStart=1; //  Проект запущен  
    }
    Temperature();   //  процедуры для конечного автомата Автомат измерения температуры  DS18x20
    Humanity();   //  процедуры для конечного автомата Автомат измерения влажности
    Display();   //  процедуры для конечного автомата Автомат вывода на дисплей и светодиодную матрицу
    ADC();   //  процедуры для конечного автомата Измерение температуры при помощи АЦП
}

aClockArduino() следует запускать в бесконечном цикле loop() {} основной программы Arduino. Кроме того, в бесконечный цикл loop() {} следует поместить функцию aClockArduino_Mills(). Имена достаточно длинные, что бы ошибиться при наборе, или же придется листать простыню кода. К счастью, есть более простой путь: в документе Visio приведены "Рекомендации по интеграции" в проект Arduino.

Текстовый блок рекомендаций по интеграции в проект Arduino
Текстовый блок рекомендаций по интеграции в  проект Arduino
Текстовый блок рекомендаций по интеграции в проект Arduino

Текстовое поле подсказки автоматически заполняется при каждой трансляции и содержит актуальные имена функций, их легко можно скопировать для вставки в проект Arduino. Дальнейшие действия потребуют подключения пользовательских функций (например, той жеDS18x20_start(). Что бы автомат их "признал", следует добавлять описание функции в блок HEADER, так же размещенный на листе графического документа Visio. Это можно сделать "скопом", разместив в отдельном программном модуле и воспользовавшись директивой компилятора #include, либо каждую в отдельности, как показано на рисунке.

Блок HEADER
Текстовый блок HEADER
Текстовый блок HEADER

Помимо текстового поля HEADER, к вашим услугам текстовое поле VARIABLE, куда можно быстро, по мере создания графического кода, размещать переменные (используя стандартные нотации языка Си). А текстовый блок конфигурации, например, позволяет выбрать язык комментариев и даже определиться с IDE результирующего кода - вторым вариантом доступен Keil uVision.

Блок конфигурации документа
Блок конфигурации документа
Блок конфигурации документа

Заключение.

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

Фотографии

часов-метеостанции, чьи конечные автоматы рассмотрены в статье. Этот хобби-проект еще в промежуточной фазе, поэтому прошу быть снисходительным к внешнему виду.

Фотографии часов-метеостанции
Плата ESP32 + внутренние датчики влажности и температуры
Плата ESP32 + внутренние датчики влажности и температуры
Дисплей в спальной комнате
Дисплей в спальной комнате
Светодиодная панель в гостиной
Светодиодная панель в гостиной
Веб страница настройки светодиодной панели в гостиной
Веб страница настройки светодиодной панели в гостиной

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

Комментарии (4)


  1. Chupakabra303
    18.08.2023 07:49

    Если велосипедами интересоваться, порекомендовал бы что-то типа https://openplcproject.com/. Там заявлена поддержка языков IEC 61131-3.


    1. DonEgen Автор
      18.08.2023 07:49

      Спасибо, загляну. Хотя ПЛК это не совсем то, для чего предназначена V2S. В ней упор сделан скорее на написание программ взаимодействия с оператором (всякие меню), поддержка протоколов передачи данных и прочее, где наиболее сильны именно конечные автоматы.


  1. hardtop
    18.08.2023 07:49
    +2

    Как то с ребенком занимались визуальным программированием для лего mindstorm. Простые вещи — да, наглядно. Любое усложнение алгоритма — ад. Никогда больше!


    1. DonEgen Автор
      18.08.2023 07:49

      В статье рассмотрен так называемый SW_Fast_Automat, которых на листе с десяток может быть. А в базовом варианте на лист устанавливается блок SW_Auto и весь лист становится одним большим автоматом. Мало одного листа? Этот же блок ставится на следующие листы и сложность автомата возрастает дальше. Вот где ад! Но куда деваться? Программа с тем же функционалом вообще не воспринимается посторонними, а в графическом варианте ее можно обсуждать хоть с руководством, хоть с заказчиком.