Skip to content

Repository files navigation

Генератор C-кода для дискретных систем управления по XML-схеме Общее описание Данное решение представляет собой консольную утилиту на C++, которая преобразует XML-сериализацию блок-схемы (Simulink‑подобный формат) в исполняемый код на языке C. Генератор обеспечивает корректную семантику дискретного моделирования, учитывая порядок вычисления комбинационных блоков и обновления состояния элементов задержки (UnitDelay).

Целевая система – ПИ-регулятор, но архитектура позволяет легко расширять поддержку новых типов блоков.

Выбор библиотек

Библиотека Назначение Причины выбора pugixml Парсинг XML-файлов 1. Простота: header-only (или один .cpp), не требует сложной настройки. 2. Быстродействие: парсер написан с фокусом на производительность, что важно для больших схем. 3. Удобный API: поиск узлов по атрибутам (find_child_by_attribute), навигация по дереву, читаемый синтаксис. 4. Портативность: работает на всех основных платформах без дополнительных зависимостей. 5. Совместимость с C++11: мы использовали стандарт C++11, и pugixml поддерживает его. STL (стандартная библиотека C++) Структуры данных, алгоритмы, ввод/вывод 1. Универсальность: все контейнеры (vector, unordered_map, set) и алгоритмы (sort, queue) есть в стандарте. 2. Надёжность: проверенные реализации, минимальный риск ошибок. 3. Отсутствие внешних зависимостей: не нужно добавлять Boost или другие фреймворки – только STL и pugixml. 4. Переносимость: код компилируется любым современным компилятором. Альтернативы (TinyXML, RapidXML, Qt XML) были отвергнуты либо из‑за большего размера, либо из‑за менее удобного API, либо из‑за необходимости дополнительных библиотек. pugixml обеспечивает оптимальный баланс.

Архитектура решения

Решение разделено на три основных этапа:

** Парсинг XML (loadXML) **

Чтение файла, извлечение блоков (Block) и их параметров (P). Извлечение линий (Line) и ветвлений (Branch), при этом мы ищем элементы

по имени атрибута (Src, Dst), а не по позиции. Это гарантирует корректный разбор даже при нестандартном порядке тегов. Запоминание типов блоков: Inport, Outport, UnitDelay для дальнейшей обработки. Построение графа зависимостей и определение порядка (computeOrder) Граф строится только для комбинационных блоков (все, кроме UnitDelay). Рёбра, ведущие в UnitDelay, игнорируются, чтобы разорвать обратные связи – это ключевое решение для корректной симуляции дискретных систем. Применяется топологическая сортировка (алгоритм Кана) для получения порядка вычислений. Отдельно формируется список UnitDelay, которые будут обновляться после всех комбинационых блоков.

** Генерация кода (generateCode) **

Создаётся структура nwocg с полями для всех сигналов. Генерируется функция nwocg_generated_init() – обнуление состояний UnitDelay. Генерируется функция nwocg_generated_step(): сначала вычисляются все комбинационные блоки в порядке из п.2, затем обновляются UnitDelay. Формируется массив внешних портов ext_ports для связи с окружением (входы/выходы).

** Ключевые алгоритмические решения **

  1. Обработка неполных XML-данных Для блоков Sum параметр Inputs (строка знаков) является семантически важным. В XML он может отсутствовать (как для Add2 и Add3 в примере). Генератор выводит предупреждение и использует "++" по умолчанию – это соответствует визуальной схеме, где все входы складываются. Аналогично, для Gain параметр Gain обязателен; если отсутствует, используется 1.

  2. Безопасный парсинг строк При разборе Src и Dst мы проверяем наличие символов # и : и пустоту номера порта перед вызовом stoi, что предотвращает исключения std::invalid_argument. Используем find_child_by_attribute для поиска конкретных

    , что исключает ошибки при изменении порядка элементов.

  3. Разрыв обратных связей В дискретных системах UnitDelay должен обновляться после того, как все комбинационные блоки, использующие его старое значение, будут вычислены. Игнорирование рёбер, входящих в UnitDelay, при топологической сортировке позволяет получить правильный порядок, сохраняя при этом информацию о том, какой сигнал подаётся на вход задержки (используется при генерации присваивания).

  4. Поддержка констант Блоки типа Constant обрабатываются отдельно – их значение подставляется непосредственно в выражения, что улучшает читаемость кода и позволяет оптимизатору работать с константами.

** Соответствие требованиям тестового задания **

Семантическая полнота – генератор учитывает знаки в сумматорах, коэффициенты усиления, период дискретизации, порядок вычислений. Обработка неявных параметров – отсутствующие Inputs не приводят к ошибке, а интерпретируются разумно. Корректное моделирование UnitDelay – задержка обновляется в конце шага, что соответствует 1/z. Портативность – код генерируется на чистом C без зависимостей от рантайма, кроме стандартной библиотеки. Гибкость – добавление новых типов блоков требует лишь расширения секции генерации в generateCode.

** Масштабируемость и поддерживаемость **

Код модульный: парсинг, построение порядка и генерация изолированы. Чёткое разделение на фазы позволяет легко модифицировать каждый этап независимо. Использование контейнеров STL обеспечивает удобство добавления новых атрибутов блоков. При необходимости можно расширить на поддержку сложных типов (матричные операции, тригонометрические функции и т.д.).

** Результат тестирования на примере ПИ-регулятора **

Входной XML-файл pi_controller.xml был успешно обработан, на выходе получен файл pi_controller.c. Сгенерированный код: Правильно вычисляет Add1, усиления, период дискретизации. Использует UnitDelay1 в Add2 до его обновления. Обновляет UnitDelay1 в конце шага. Формирует массив внешних портов с корректными указателями.

Пример выходного кода (сокращённо):

void nwocg_generated_step()
{
    nwocg.Add1 = nwocg.setpoint - nwocg.feedback;
    nwocg.P_gain = nwocg.Add1 * 3;
    nwocg.I_gain = nwocg.Add1 * 2;
    nwocg.Ts = nwocg.I_gain * 0.01;
    nwocg.Add2 = nwocg.Ts + nwocg.Unit_Delay1;
    nwocg.Add3 = nwocg.P_gain + nwocg.Add2;
    nwocg.Unit_Delay1 = nwocg.Add2;
}

Это полностью соответствует дискретной модели ПИ-регулятора.

Инструкция по сборке и использованию Убедитесь, что в директории проекта есть файлы pugixml.hpp, pugiconfig.hpp и pugixml.cpp.

** Скомпилируйте генератор: **

g++ -std=c++11 -O2 generator.cpp pugixml.cpp -o generator

Запустите:

./generator модель.xml выходной_файл.c

Полученный .c файл можно включить в проект моделирования, обеспечив наличие заголовочного файла nwocg_run.h с определением структуры nwocg_ExtPort.

Заключение Предложенное решение успешно решает задачу генерации C-кода из XML-схемы, учитывая специфику дискретного моделирования

About

code generator DSL

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages