Генерация кода в Engee
Генерация кода через командную строку и интерактивный скрипт
Для генерации кода в Engee также можно использовать команду engee.generate_code в командной строке
или в редакторе скриптов
.
Чтобы сгенерировать код для подсистемы, укажите полный путь к подсистеме с помощью аргумента subsystem_name. Полный путь к подсистеме включает ее имя в модели.
engee.generate_code(
model_path::String,
output_dir::String;
subsystem_name::String = nothing,
subsystem_id::String = nothing,
target::String = "c",
template_path::String = ""
)
Аргументы:
path/to/modelname.engee::String: абсолютный или относительный путь к модели, из которой генерируется код. В качестве аргумента может выступать объект модели (объект типаmodel, полученный функциейengee.gcm).path/to/output_dir::String: абсолютный или относительный путь к директории, в которую сохранится сгенерированный код. Если директорииoutput_dirне существует, то она будет создана автоматически.subsystem_name=path/to/subsystem::String: полный путь к атомарной подсистеме, из которой генерируется код.subsystem_id = subsystem_id::String: уникальный идентификатор атомарной подсистемы, из которой генерируется код.target::String: указание языка для генерации кода. Поддерживаемые языки — Си (по умолчанию) или Verilog.template_path::String: путь к пользовательскому.jl-шаблону (например, с функциейmain), используемому при генерации кода. Если путь не указан, то используется шаблон по умолчанию.
Если указаны параметры subsystem_name или subsystem_id, то выполняется генерация кода для подсистемы. Если ни один из них не указан, то генерация выполняется для всей модели.
Рассмотрим несколько примеров.
Абсолютный путь к модели:
engee.generate_code("/user/newmodel_1.engee", "/user/newmodel_1/codegen_output", subsystem_name = "Subsystem")
Относительный путь к модели:
engee.generate_code("newmodel_1.engee", "newmodel_1/codegen_output", subsystem_name = "Subsystem")
Если модель уже открыта в Engee, то можно передать объект модели, полученный с помощью функции engee.gcm:
m = engee.gcm()
engee.generate_code(m, "/user/newmodel_1/codegen_output", subsystem_name = "Subsystem")
Генерация кода для атомарной подсистемы по ее id:
engee.generate_code(
"/user/newmodel_1.engee",
"/user/newmodel_1/Subsystem";
subsystem_name = "Subsystem",
subsystem_id = "88275e0b-a049-4bb5-b8c7-057badd1b536"
)
Если указан неверный subsystem_id, то генерация завершится с ошибкой, даже если subsystem_name указан правильно. В таких случаях Engee не сможет найти подсистему, и код не будет сгенерирован. Поэтому надежнее использовать только subsystem_name (если он уникален в модели).
Практическая часть
Модель сглаживающего фильтра нижних частот
Рассмотрим модель lowpass_filter.engee, представляющую собой сглаживающий фильтр нижних частот. Модель имеет вид:
В ней основной сигнал (синусоида амплитудой 1 и частотой 1 рад/с) складывается с шумовым сигналом (синусоида амплитудой 0.1 и частотой 10 рад/с) и поступает в подсистему SubSystem, в которой выполняется сглаживание сигнала:
Результаты работы модели (зашумленная и сглаженная синусоиды) приведены на графике:
✏️Задание 1
Сгенерируйте Си-код для подсистемы SubSystem модели lowpass_filter.engee:
- Откройте модель
lowpass_filter.engee, находящуюся в папке данного курса. - В окне настроек модели проверьте, что во вкладке Генерация кода значение параметра Целевая платформа выбрано равным
Си. - Сгенерируйте Си-код для подсистемы SubSystem. Для этого в интерактивном скрипте выполните следующую команду:
engee.generate_code("$(@__DIR__)/lowpass_filter.engee", "$(@__DIR__)/lowpass_filter_code", subsystem_name = "SubSystem")
В результате в текущей папке должна появиться папка lowpass_filter_code, в которой будут находиться три файла:
main.c;lowpass_filter_SubSystem.c;lowpass_filter_SubSystem.h.
Решение
engee.generate_code("$(@__DIR__)/lowpass_filter.engee", "$(@__DIR__)/lowpass_filter_code", subsystem_name="SubSystem")
Файл main.c:
/* Code generated by Engee
* Model name: lowpass_filter.engee
* Code generator: release-1.1.24
* Date: Mon Sep 22 13:28:13 2025 GMT
*/
#include "lowpass_filter_SubSystem.h"
void do_step(void)
{
static bool OverrunFlag = false;
/* Check for overrun */
if (OverrunFlag) {
return;
}
OverrunFlag = true;
/* Step the model */
lowpass_filter_SubSystem_step();
/* Indicate task complete */
OverrunFlag = false;
}
int main(int argc, char *argv[])
{
(void) argc;
(void) argv;
/* Initialize model */
lowpass_filter_SubSystem_init();
while (1) {
/* Perform application tasks here */
}
/* Terminate model */
lowpass_filter_SubSystem_term();
return 0;
}
Файл lowpass_filter_SubSystem.c:
/* Code generated by Engee
* Model name: lowpass_filter.engee
* Code generator: release-1.1.24
* Date: Mon Sep 22 13:28:13 2025 GMT
*/
#include "lowpass_filter_SubSystem.h"
/* External inputs */
Ext_lowpass_filter_SubSystem_U lowpass_filter_SubSystem_U;
/* External outputs */
Ext_lowpass_filter_SubSystem_Y lowpass_filter_SubSystem_Y;
/* Block states */
Ext_lowpass_filter_SubSystem_S lowpass_filter_SubSystem_S;
/* Model initialize function */
void lowpass_filter_SubSystem_init() {
/* InitializeConditions for Delay: /Задержка */
for (int i = 0; i < 50; i++) {
lowpass_filter_SubSystem_S.Delay_state[i] = 0.0;
}
}
/* Model terminate function */
void lowpass_filter_SubSystem_term() {
/* (no terminate code required) */
}
/* Model step function */
void lowpass_filter_SubSystem_step() {
double Delay;
int Delay_ix;
double Gain;
double Gain_1;
double Sum;
/* Delay: /Задержка incorporates:
* Inport: /In1
*/
Delay = lowpass_filter_SubSystem_S.Delay_state[0];
/* Gain: /Усиление 1 incorporates:
* Inport: /In1
*/
Gain = 0.5 * lowpass_filter_SubSystem_U.In1;
/* Gain: /Усиление 2 incorporates:
* Delay: /Задержка
*/
Gain_1 = 0.5 * Delay;
/* Sum: /Сумматор 2 incorporates:
* Gain: /Усиление 1
* Gain: /Усиление 2
*/
Sum = Gain + Gain_1;
/* Outport: /Out1 incorporates:
* Sum: /Сумматор 2
*/
lowpass_filter_SubSystem_Y.Out1 = Sum;
/* Update for Delay: /Задержка */
for (Delay_ix = 0; Delay_ix < 49; Delay_ix++) {
lowpass_filter_SubSystem_S.Delay_state[Delay_ix] = lowpass_filter_SubSystem_S.Delay_state[Delay_ix + 1];
}
lowpass_filter_SubSystem_S.Delay_state[49] = lowpass_filter_SubSystem_U.In1;
}
Файл lowpass_filter_SubSystem.h:
/* Code generated by Engee
* Model name: lowpass_filter.engee
* Code generator: release-1.1.24
* Date: Mon Sep 22 13:28:13 2025 GMT
*/
#ifndef HEADER_lowpass_filter_SubSystem_h
#define HEADER_lowpass_filter_SubSystem_h
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>
#include <float.h>
#include <limits.h>
#include <string.h>
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
typedef struct {
double In1; /* /In1 */
} Ext_lowpass_filter_SubSystem_U;
/* External inputs */
extern Ext_lowpass_filter_SubSystem_U lowpass_filter_SubSystem_U;
typedef struct {
double Out1; /* /Out1 */
} Ext_lowpass_filter_SubSystem_Y;
/* External outputs */
extern Ext_lowpass_filter_SubSystem_Y lowpass_filter_SubSystem_Y;
typedef struct {
double Delay_state[50]; /* /Задержка */
} Ext_lowpass_filter_SubSystem_S;
/* Block states */
extern Ext_lowpass_filter_SubSystem_S lowpass_filter_SubSystem_S;
/* Model entry point functions */
extern void lowpass_filter_SubSystem_init();
extern void lowpass_filter_SubSystem_step();
extern void lowpass_filter_SubSystem_term();
/* Here is the system hierarchy for this model:
*
* 'lowpass_filter_SubSystem' : '/'
*/
#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */
#endif /* HEADER_lowpass_filter_SubSystem_h */
Модель системы управления автопилотом
Рассмотрим теперь модель системы управления автопилотом по оси крена aircraft_roll.engee. Эта модель может работать в двух режимах работы: стабилизация крена и удержание курса полета. Режимы стабилизации крена и удержания курса полета архитектурно выделены в атомарные подсистемы внутри модели.
Функция стабилизации крена — это ПИД-регулятор, который использует обратную связь по углу и скорости крена для выдачи команды элерону. Входным сигналом для регулятора является либо уставка (заданное значение) угла крена, либо команда угла крена для удержания желаемого курса полета. Модель выглядит следующим образом:
Мы видим тестовую обвязку вокруг подсистемы autopilot_roll, которая представляет собой алгоритмическую часть модели. Эта подсистема имеет вид:
В подсистеме RollMode реализован базовый расчет уставки угла крена. В зависимости от типа подсистемы генератор кода Engee встроит это вычисление непосредственно в основную функцию модели (для виртуальной подсистемы) или сгенерирует отдельную функцию с именем подсистемы (для атомарной подсистемы).
В подсистеме HeadingMode вычисляется уставка крена для удержания желаемого курса полета.
В подсистеме CtlMode реализуется функция стабилизации угла крена в виде ПИД-регулятора.
✏️Задание 2
Сгенерируйте Си-код для подсистемы autopilot_roll модели aircraft_roll.engee:
- Откройте модель
aircraft_roll.engee, находящуюся в папке данного курса. - Запустите симуляцию модели и постройте графики выходных сигналов.
- В окне настроек модели проверьте, что во вкладке Генерация кода значение параметра Целевая платформа выбрано равным
Си. - Сгенерируйте Си-код для подсистемы autopilot_roll, выполнив соответствующую команду в интерактивном скрипте. Укажите папку
autopilot_roll_codeдля записи сгенерированных файлов.
В результате в текущей папке должна появиться папка autopilot_roll_code, в которой будут находиться три файла:
main.c;aircraft_roll_autopilot_roll.c;aircraft_roll_autopilot_roll.h.
Решение
engee.generate_code("$(@__DIR__)/aircraft_roll.engee", "$(@__DIR__)/autopilot_roll_code", subsystem_name="autopilot_roll")
Файл main.c:
/* Code generated by Engee
* Model name: aircraft_roll.engee
* Code generator: release-1.1.24
* Date: Fri Sep 26 15:35:55 2025 GMT
*/
#include "aircraft_roll_autopilot_roll.h"
void do_step(void)
{
static bool OverrunFlag = false;
/* Check for overrun */
if (OverrunFlag) {
return;
}
OverrunFlag = true;
/* Step the model */
aircraft_roll_autopilot_roll_step();
/* Indicate task complete */
OverrunFlag = false;
}
int main(int argc, char *argv[])
{
(void) argc;
(void) argv;
/* Initialize model */
aircraft_roll_autopilot_roll_init();
while (1) {
/* Perform application tasks here */
}
/* Terminate model */
aircraft_roll_autopilot_roll_term();
return 0;
}
Файл aircraft_roll_autopilot_roll.c:
/* Code generated by Engee
* Model name: aircraft_roll.engee
* Code generator: release-1.1.24
* Date: Fri Sep 26 15:35:55 2025 GMT
*/
#include "aircraft_roll_autopilot_roll.h"
/* External inputs */
Ext_aircraft_roll_autopilot_roll_U aircraft_roll_autopilot_roll_U;
/* External outputs */
Ext_aircraft_roll_autopilot_roll_Y aircraft_roll_autopilot_roll_Y;
/* Block states */
Ext_aircraft_roll_autopilot_roll_S aircraft_roll_autopilot_roll_S;
/* Model initialize function */
void aircraft_roll_autopilot_roll_init() {
/* InitializeConditions for UnitDelay: /RollMode/Unit Delay */
aircraft_roll_autopilot_roll_S.UnitDelay_state = 0.0;
/* InitializeConditions for UnitDelay: /CtlMode/Unit Delay */
aircraft_roll_autopilot_roll_S.UnitDelay_1_state = 0.0;
}
/* Model terminate function */
void aircraft_roll_autopilot_roll_term() {
/* (no terminate code required) */
}
/* Model step function */
void aircraft_roll_autopilot_roll_step() {
double UnitDelay;
double UnitDelay_1;
int64_t Abs;
bool NotEngaged;
bool NotEngaged_1;
bool RelationalOperator_1;
bool RelationalOperator;
double hdgError;
double DispGain_1;
bool OR;
bool RelationalOperator_2;
double Switch_1;
double Saturation_3;
double Switch;
int64_t Switch_2;
double Switch_1_1;
int64_t ModeSwitch;
int64_t Saturation;
double Add_4;
double Gain_3;
double Saturation_1;
double Add;
double Gain;
double Gain_4;
double Ts_1;
double Add_1_1;
double Saturation_2;
double Add_2;
double Switch_1_2;
double Switch_2_1;
/* Output for UnitDelay: /RollMode/Unit Delay */
UnitDelay = aircraft_roll_autopilot_roll_S.UnitDelay_state;
/* Output for UnitDelay: /CtlMode/Unit Delay */
UnitDelay_1 = aircraft_roll_autopilot_roll_S.UnitDelay_1_state;
/* Abs: /RollMode/Abs */
Abs = fabs(aircraft_roll_autopilot_roll_U.Turn_Knob);
/* Logic: /RollMode/NotEngaged incorporates:
* Inport: /AP_Eng
*/
NotEngaged = !((aircraft_roll_autopilot_roll_U.AP_Eng != 0.0));
/* Logic: /CtlMode/NotEngaged incorporates:
* Inport: /AP_Eng
*/
NotEngaged_1 = !((aircraft_roll_autopilot_roll_U.AP_Eng != 0.0));
/* RelationalOperator: /RollMode/Relational Operator-1 incorporates:
* Inport: /Phi
* Constant: /RollMode/LoThr-1
*/
RelationalOperator_1 = aircraft_roll_autopilot_roll_U.Phi <= -6ll;
/* RelationalOperator: /RollMode/Relational Operator incorporates:
* Inport: /Phi
* Constant: /RollMode/UpThr-1
*/
RelationalOperator = aircraft_roll_autopilot_roll_U.Phi >= 6ll;
/* Sum: /HeadingMode/Add-1 incorporates:
* Inport: /HDG_Ref
* Inport: /Psi
*/
hdgError = aircraft_roll_autopilot_roll_U.HDG_Ref - aircraft_roll_autopilot_roll_U.Psi;
/* Gain: /HeadingMode/DispGain-1 incorporates:
* Sum: /HeadingMode/Add-1
*/
DispGain_1 = 0.015 * hdgError;
/* Logic: /RollMode/OR incorporates:
* RelationalOperator: /RollMode/Relational Operator
* RelationalOperator: /RollMode/Relational Operator-1
*/
OR = RelationalOperator || RelationalOperator_1;
/* RelationalOperator: /RollMode/Relational Operator-2 incorporates:
* Abs: /RollMode/Abs
* Constant: /RollMode/Constant-2
*/
RelationalOperator_2 = Abs < 3ll;
/* Switch: /CtlMode/Switch-1 incorporates:
* Constant: /CtlMode/Constant-2
* Logic: /CtlMode/NotEngaged
* UnitDelay: /CtlMode/Unit Delay
*/
if (NotEngaged_1 > false) {
Switch_1 = 0.0;
} else {
Switch_1 = UnitDelay_1;
}
/* End of Switch: /CtlMode/Switch-1 */
/* Product: /HeadingMode/Product incorporates:
* Gain: /HeadingMode/DispGain-1
* Inport: /TAS
*/
const double Product = DispGain_1 * aircraft_roll_autopilot_roll_U.TAS;
/* Saturate: /CtlMode/Saturation-3 incorporates:
* Switch: /CtlMode/Switch-1
*/
if (Switch_1 < -5) {
Saturation_3 = -5;
} else if (Switch_1 > 5) {
Saturation_3 = 5;
} else {
Saturation_3 = Switch_1;
}
/* Switch: /RollMode/Switch incorporates:
* Inport: /Phi
* Logic: /RollMode/OR
* Constant: /RollMode/Constant
*/
if (OR != false) {
Switch = aircraft_roll_autopilot_roll_U.Phi;
} else {
Switch = 0.0;
}
/* End of Switch: /RollMode/Switch */
/* Switch: /RollMode/Switch-2 incorporates:
* UnitDelay: /RollMode/Unit Delay
* RelationalOperator: /RollMode/Relational Operator-2
* Inport: /Turn_Knob
*/
if (RelationalOperator_2 != false) {
Switch_2 = UnitDelay;
} else {
Switch_2 = aircraft_roll_autopilot_roll_U.Turn_Knob;
}
/* End of Switch: /RollMode/Switch-2 */
/* Switch: /RollMode/Switch-1 incorporates:
* Switch: /RollMode/Switch
* Logic: /RollMode/NotEngaged
* UnitDelay: /RollMode/Unit Delay
*/
if (NotEngaged > false) {
Switch_1_1 = Switch;
} else {
Switch_1_1 = UnitDelay;
}
/* End of Switch: /RollMode/Switch-1 */
/* Switch: /ModeSwitch incorporates:
* Product: /HeadingMode/Product
* Inport: /HDG_Mode
* Switch: /RollMode/Switch-2
*/
if (aircraft_roll_autopilot_roll_U.HDG_Mode != 0.0) {
ModeSwitch = Product;
} else {
ModeSwitch = Switch_2;
}
/* End of Switch: /ModeSwitch */
/* Saturate: /CtlMode/Saturation incorporates:
* Switch: /ModeSwitch
*/
if (ModeSwitch < -30) {
Saturation = -30;
} else if (ModeSwitch > 30) {
Saturation = 30;
} else {
Saturation = ModeSwitch;
}
/* Sum: /CtlMode/Add-4 incorporates:
* Saturate: /CtlMode/Saturation
* Inport: /Phi
*/
Add_4 = (double)(Saturation) - aircraft_roll_autopilot_roll_U.Phi;
/* Gain: /CtlMode/Gain-3 incorporates:
* Sum: /CtlMode/Add-4
*/
Gain_3 = 0.75 * Add_4;
/* Saturate: /CtlMode/Saturation-1 incorporates:
* Gain: /CtlMode/Gain-3
*/
if (Gain_3 < -6) {
Saturation_1 = -6;
} else if (Gain_3 > 6) {
Saturation_1 = 6;
} else {
Saturation_1 = Gain_3;
}
/* Sum: /CtlMode/Add incorporates:
* Saturate: /CtlMode/Saturation-1
* Inport: /Rate_FB
*/
Add = Saturation_1 - aircraft_roll_autopilot_roll_U.Rate_FB;
/* Gain: /CtlMode/Gain incorporates:
* Sum: /CtlMode/Add
*/
Gain = 0.5 * Add;
/* Gain: /CtlMode/Gain-4 incorporates:
* Sum: /CtlMode/Add
*/
Gain_4 = 2ll * Add;
/* Gain: /CtlMode/Ts-1 incorporates:
* Gain: /CtlMode/Gain
*/
Ts_1 = 0.025 * Gain;
/* Sum: /CtlMode/Add-1 incorporates:
* Saturate: /CtlMode/Saturation-3
* Gain: /CtlMode/Gain-4
*/
Add_1_1 = Saturation_3 + Gain_4;
/* Saturate: /CtlMode/Saturation-2 incorporates:
* Sum: /CtlMode/Add-1
*/
if (Add_1_1 < -15) {
Saturation_2 = -15;
} else if (Add_1_1 > 15) {
Saturation_2 = 15;
} else {
Saturation_2 = Add_1_1;
}
/* Sum: /CtlMode/Add-2 incorporates:
* Gain: /CtlMode/Ts-1
* UnitDelay: /CtlMode/Unit Delay
*/
Add_2 = Ts_1 + UnitDelay_1;
/* Switch: /CtlMode/Switch incorporates:
* Constant: /CtlMode/Constant
* Logic: /CtlMode/NotEngaged
* Sum: /CtlMode/Add-2
*/
if (NotEngaged_1 > false) {
Switch_1_2 = 0.0;
} else {
Switch_1_2 = Add_2;
}
/* End of Switch: /CtlMode/Switch */
/* Switch: /Switch incorporates:
* Saturate: /CtlMode/Saturation-2
* Inport: /AP_Eng
* Constant: /Constant
*/
if (aircraft_roll_autopilot_roll_U.AP_Eng != 0.0) {
Switch_2_1 = Saturation_2;
} else {
Switch_2_1 = 0ll;
}
/* End of Switch: /Switch */
/* Outport: /Ail_Cmd incorporates:
* Switch: /Switch
*/
aircraft_roll_autopilot_roll_Y.Ail_Cmd = Switch_2_1;
/* Update for UnitDelay: /RollMode/Unit Delay */
aircraft_roll_autopilot_roll_S.UnitDelay_state = Switch_1_1;
/* Update for UnitDelay: /CtlMode/Unit Delay */
aircraft_roll_autopilot_roll_S.UnitDelay_1_state = Switch_1_2;
}
Файл aircraft_roll_autopilot_roll.h:
/* Code generated by Engee
* Model name: aircraft_roll.engee
* Code generator: release-1.1.24
* Date: Fri Sep 26 15:35:55 2025 GMT
*/
#ifndef HEADER_aircraft_roll_autopilot_roll_h
#define HEADER_aircraft_roll_autopilot_roll_h
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>
#include <float.h>
#include <limits.h>
#include <string.h>
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
typedef struct {
double Phi; /* /Phi */
double Psi; /* /Psi */
double Rate_FB; /* /Rate_FB */
double TAS; /* /TAS */
double AP_Eng; /* /AP_Eng */
double HDG_Mode; /* /HDG_Mode */
double HDG_Ref; /* /HDG_Ref */
int64_t Turn_Knob; /* /Turn_Knob */
} Ext_aircraft_roll_autopilot_roll_U;
/* External inputs */
extern Ext_aircraft_roll_autopilot_roll_U aircraft_roll_autopilot_roll_U;
typedef struct {
double Ail_Cmd; /* /Ail_Cmd */
} Ext_aircraft_roll_autopilot_roll_Y;
/* External outputs */
extern Ext_aircraft_roll_autopilot_roll_Y aircraft_roll_autopilot_roll_Y;
typedef struct {
double UnitDelay_state; /* /RollMode/Unit Delay */
double UnitDelay_1_state; /* /CtlMode/Unit Delay */
} Ext_aircraft_roll_autopilot_roll_S;
/* Block states */
extern Ext_aircraft_roll_autopilot_roll_S aircraft_roll_autopilot_roll_S;
/* Model entry point functions */
extern void aircraft_roll_autopilot_roll_init();
extern void aircraft_roll_autopilot_roll_step();
extern void aircraft_roll_autopilot_roll_term();
/* Here is the system hierarchy for this model:
*
* 'aircraft_roll_autopilot_roll' : '/'
* '' : '/HeadingMode'
* '' : '/RollMode'
* '' : '/CtlMode'
*/
#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */
#endif /* HEADER_aircraft_roll_autopilot_roll_h */