Документация Engee
Notebook

Преобразование модели MATLAB Simulink в модель Engee

Введение

В этом примере мы рассмотрим процесс автоматического переноса пользовательских наработок из MATLAB Simulink в Engee, а также сравним результат переноса, промоделировав исходную и полученную модели.

Получение скрипта для построения модели

Первый шаг переноса модели из Simulink – автоматическое получение скрипта для построения модели Engee при помощи командного управления. Для начала определим переменные для файлов этого примера:

In [ ]:
путь_примера = "$(@__DIR__)"                                # Путь к папке примера
имя_модели = "alphabetafilter"                              # Имя моделей
модельSLX = путь_примера*"/slx/"*имя_модели*".slx";         # Путь к исходной модели

mkdir(joinpath(путь_примера,"engee"));                      # Создаём папку для результирующей модели
скриптJL = путь_примера*"/engee/convert_script.jl";         # Путь к результирующему скрипту 
модельENGEE = путь_примера*"/engee/"*имя_модели*".engee";   # Путь к результирующей модели 

Модель MATLAB Simulink, которую мы используем в данном примере, – alphabetafilter.slx из примера интеграции кода Си в модель Engee.

Теперь воспользуемся функцией программного управления engee.convert_model(). Эта функция сначала определяет содержимое исходной модели – подсистемы, блоки, параметры, связи блоков и подсистем. Затем на основе полученных данных она создаёт скрипт .jl с командами программного управления для построения новой модели .engee

In [ ]:
engee.convert_model(модельSLX, скриптJL);           # Получение скрипта .jl для автоматического создания модели Engee
[ Info: Generated conversion script: /user/start/examples/project_management/slx_to_engee_conversion/engee/convert_script.jl

⚠️ При конвертации моделей стоит учитывать, что в текущем релизе Engee:

In [ ]:
engee.version()
Out[0]:
"25.1.0"

поддерживается конвертация блоков, поддерживаемых генератором кода. В дальнейшем также будет реализована поддержка конвертации для вcех блоков библиотеки Engee.

Содержимое полученного скрипта приведено ниже. В коде для автоматического построения модели .engee используются следующие функции:

Дополнительно с программным управлением можно ознакомиться в соответствующем примере.

Результирующий код

model = engee.create("alphabetafilter")

engee.add_block("/Basic/Ports & Subsystems/In1", "alphabetafilter/processNoiseVariance") engee.set_param!("alphabetafilter/processNoiseVariance", "SignalType"=>"real")
engee.add_block("/Basic/Ports & Subsystems/In1", "alphabetafilter/measurementNoiseVariance") engee.set_param!("alphabetafilter/measurementNoiseVariance", "SignalType"=>"real")
engee.add_block("/Basic/Ports & Subsystems/In1", "alphabetafilter/y") engee.set_param!("alphabetafilter/y", "SignalType"=>"real")
engee.add_block("/Basic/Ports & Subsystems/Subsystem", "alphabetafilter/determineAlphaBeta")
engee.delete_contents("alphabetafilter/determineAlphaBeta")
engee.add_block("/Basic/Ports & Subsystems/In1", "alphabetafilter/determineAlphaBeta/processNoiseVariance") engee.set_param!("alphabetafilter/determineAlphaBeta/processNoiseVariance", "SignalType"=>"real")
engee.add_block("/Basic/Ports & Subsystems/In1", "alphabetafilter/determineAlphaBeta/measurementNoiseVariance") engee.set_param!("alphabetafilter/determineAlphaBeta/measurementNoiseVariance", "SignalType"=>"real")
engee.add_block("/Basic/Math Operations/Add", "alphabetafilter/determineAlphaBeta/Add")
engee.add_block("/Basic/Math Operations/Add", "alphabetafilter/determineAlphaBeta/Add1") engee.set_param!("alphabetafilter/determineAlphaBeta/Add1", "Inputs"=>"+-")
engee.add_block("/Basic/Math Operations/Add", "alphabetafilter/determineAlphaBeta/Add2") engee.set_param!("alphabetafilter/determineAlphaBeta/Add2", "Inputs"=>"+-")
engee.add_block("/Basic/Math Operations/Bias", "alphabetafilter/determineAlphaBeta/Bias") engee.set_param!("alphabetafilter/determineAlphaBeta/Bias", "Bias"=>"4")
engee.add_block("/Basic/Math Operations/Bias", "alphabetafilter/determineAlphaBeta/Bias1") engee.set_param!("alphabetafilter/determineAlphaBeta/Bias1", "Bias"=>"-1")
engee.add_block("/Basic/Math Operations/Bias", "alphabetafilter/determineAlphaBeta/Bias2") engee.set_param!("alphabetafilter/determineAlphaBeta/Bias2", "Bias"=>"-2")
engee.add_block("/Basic/Math Operations/Bias", "alphabetafilter/determineAlphaBeta/Bias3") engee.set_param!("alphabetafilter/determineAlphaBeta/Bias3", "Bias"=>"-1")
engee.add_block("/Basic/Sources/Constant", "alphabetafilter/determineAlphaBeta/Constant") engee.set_param!("alphabetafilter/determineAlphaBeta/Constant", "Value"=>"dt") engee.set_param!("alphabetafilter/determineAlphaBeta/Constant", "SampleTime"=>"0.0")
engee.add_block("/Basic/Math Operations/Gain", "alphabetafilter/determineAlphaBeta/Gain") engee.set_param!("alphabetafilter/determineAlphaBeta/Gain", "Gain"=>"8")
engee.add_block("/Basic/Math Operations/Gain", "alphabetafilter/determineAlphaBeta/Gain1") engee.set_param!("alphabetafilter/determineAlphaBeta/Gain1", "Gain"=>"1/4")
engee.add_block("/Basic/Math Operations/Gain", "alphabetafilter/determineAlphaBeta/Gain2") engee.set_param!("alphabetafilter/determineAlphaBeta/Gain2", "Gain"=>"-1")
engee.add_block("/Basic/Math Operations/Gain", "alphabetafilter/determineAlphaBeta/Gain3") engee.set_param!("alphabetafilter/determineAlphaBeta/Gain3", "Gain"=>"-1")
engee.add_block("/Basic/Math Operations/Gain", "alphabetafilter/determineAlphaBeta/Gain4") engee.set_param!("alphabetafilter/determineAlphaBeta/Gain4", "Gain"=>"2")
engee.add_block("/Basic/Math Operations/Gain", "alphabetafilter/determineAlphaBeta/Gain5") engee.set_param!("alphabetafilter/determineAlphaBeta/Gain5", "Gain"=>"-1")
engee.add_block("/Basic/Math Operations/Gain", "alphabetafilter/determineAlphaBeta/Gain6") engee.set_param!("alphabetafilter/determineAlphaBeta/Gain6", "Gain"=>"4")
engee.add_block("/Basic/Math Operations/Math Function", "alphabetafilter/determineAlphaBeta/Math Function") engee.set_param!("alphabetafilter/determineAlphaBeta/Math Function", "Operator"=>"square")
engee.add_block("/Basic/Math Operations/Math Function", "alphabetafilter/determineAlphaBeta/Math Function1") engee.set_param!("alphabetafilter/determineAlphaBeta/Math Function1", "Operator"=>"square")
engee.add_block("/Basic/Math Operations/Math Function", "alphabetafilter/determineAlphaBeta/Math Function2") engee.set_param!("alphabetafilter/determineAlphaBeta/Math Function2", "Operator"=>"square")
engee.add_block("/Basic/Math Operations/Math Function", "alphabetafilter/determineAlphaBeta/Math Function3") engee.set_param!("alphabetafilter/determineAlphaBeta/Math Function3", "Operator"=>"reciprocal")
engee.add_block("/Basic/Math Operations/Product", "alphabetafilter/determineAlphaBeta/Product") engee.set_param!("alphabetafilter/determineAlphaBeta/Product", "Inputs"=>"**")
engee.add_block("/Basic/Math Operations/Product", "alphabetafilter/determineAlphaBeta/Product1") engee.set_param!("alphabetafilter/determineAlphaBeta/Product1", "Inputs"=>"**")
engee.add_block("/Basic/Math Operations/Sqrt", "alphabetafilter/determineAlphaBeta/Sqrt")
engee.add_block("/Basic/Math Operations/Sqrt", "alphabetafilter/determineAlphaBeta/Sqrt1")
engee.add_block("/Basic/Ports & Subsystems/Out1", "alphabetafilter/determineAlphaBeta/alpha") engee.set_param!("alphabetafilter/determineAlphaBeta/alpha", "InitialOutput"=>"0")
engee.add_block("/Basic/Ports & Subsystems/Out1", "alphabetafilter/determineAlphaBeta/beta") engee.set_param!("alphabetafilter/determineAlphaBeta/beta", "InitialOutput"=>"0")
engee.add_line("alphabetafilter/determineAlphaBeta", "Add/1", "Sqrt/1") engee.add_line("alphabetafilter/determineAlphaBeta", "Math Function2/1", "Bias1/1") engee.add_line("alphabetafilter/determineAlphaBeta", "Gain1/1", "Math Function2/1") engee.add_line("alphabetafilter/determineAlphaBeta", "Sqrt/1", "Add1/2") engee.add_line("alphabetafilter/determineAlphaBeta", "Bias/1", "Add1/1") engee.add_line("alphabetafilter/determineAlphaBeta", "Bias1/1", "Gain2/1") engee.add_line("alphabetafilter/determineAlphaBeta", "Add2/1", "beta/1") engee.add_line("alphabetafilter/determineAlphaBeta", "Math Function/1", "Product/1") engee.add_line("alphabetafilter/determineAlphaBeta", "Gain5/1", "Sqrt1/1") engee.add_line("alphabetafilter/determineAlphaBeta", "Add1/1", "Gain1/1") engee.add_line("alphabetafilter/determineAlphaBeta", "Bias3/1", "Gain5/1") engee.add_line("alphabetafilter/determineAlphaBeta", "Product1/1", "Math Function1/1") engee.add_line("alphabetafilter/determineAlphaBeta", "Product1/1", "Gain/1") engee.add_line("alphabetafilter/determineAlphaBeta", "Product1/1", "Bias/1") engee.add_line("alphabetafilter/determineAlphaBeta", "Bias2/1", "Gain3/1") engee.add_line("alphabetafilter/determineAlphaBeta", "Product/1", "Product1/1") engee.add_line("alphabetafilter/determineAlphaBeta", "Gain/1", "Add/2") engee.add_line("alphabetafilter/determineAlphaBeta", "Sqrt1/1", "Gain6/1") engee.add_line("alphabetafilter/determineAlphaBeta", "Gain2/1", "alpha/1") engee.add_line("alphabetafilter/determineAlphaBeta", "Gain2/1", "Bias2/1") engee.add_line("alphabetafilter/determineAlphaBeta", "Gain2/1", "Bias3/1") engee.add_line("alphabetafilter/determineAlphaBeta", "Gain3/1", "Gain4/1") engee.add_line("alphabetafilter/determineAlphaBeta", "Gain6/1", "Add2/2") engee.add_line("alphabetafilter/determineAlphaBeta", "Math Function1/1", "Add/1") engee.add_line("alphabetafilter/determineAlphaBeta", "Gain4/1", "Add2/1") engee.add_line("alphabetafilter/determineAlphaBeta", "Constant/1", "Math Function/1") engee.add_line("alphabetafilter/determineAlphaBeta", "measurementNoiseVariance/1", "Math Function3/1") engee.add_line("alphabetafilter/determineAlphaBeta", "processNoiseVariance/1", "Product/2") engee.add_line("alphabetafilter/determineAlphaBeta", "Math Function3/1", "Product1/2")
engee.add_block("/Basic/Ports & Subsystems/Subsystem", "alphabetafilter/filterInputSignal")
engee.delete_contents("alphabetafilter/filterInputSignal")
engee.add_block("/Basic/Ports & Subsystems/In1", "alphabetafilter/filterInputSignal/alpha") engee.set_param!("alphabetafilter/filterInputSignal/alpha", "SignalType"=>"real")
engee.add_block("/Basic/Ports & Subsystems/In1", "alphabetafilter/filterInputSignal/beta") engee.set_param!("alphabetafilter/filterInputSignal/beta", "SignalType"=>"real")
engee.add_block("/Basic/Ports & Subsystems/In1", "alphabetafilter/filterInputSignal/y") engee.set_param!("alphabetafilter/filterInputSignal/y", "SignalType"=>"real")
engee.add_block("/Basic/Math Operations/Add", "alphabetafilter/filterInputSignal/Add")
engee.add_block("/Basic/Math Operations/Add", "alphabetafilter/filterInputSignal/Add1") engee.set_param!("alphabetafilter/filterInputSignal/Add1", "Inputs"=>"+-")
engee.add_block("/Basic/Math Operations/Add", "alphabetafilter/filterInputSignal/Add2")
engee.add_block("/Basic/Math Operations/Add", "alphabetafilter/filterInputSignal/Add3")
engee.add_block("/Basic/Math Operations/Gain", "alphabetafilter/filterInputSignal/Gain") engee.set_param!("alphabetafilter/filterInputSignal/Gain", "Gain"=>"dt")
engee.add_block("/Basic/Math Operations/Gain", "alphabetafilter/filterInputSignal/Gain1") engee.set_param!("alphabetafilter/filterInputSignal/Gain1", "Gain"=>"1/dt")
engee.add_block("/Basic/Math Operations/Product", "alphabetafilter/filterInputSignal/Product") engee.set_param!("alphabetafilter/filterInputSignal/Product", "Inputs"=>"**")
engee.add_block("/Basic/Math Operations/Product", "alphabetafilter/filterInputSignal/Product1") engee.set_param!("alphabetafilter/filterInputSignal/Product1", "Inputs"=>"**")
engee.add_block("/Basic/Discrete/Unit Delay", "alphabetafilter/filterInputSignal/Unit Delay") engee.set_param!("alphabetafilter/filterInputSignal/Unit Delay", "InitialCondition"=>"0") engee.set_param!("alphabetafilter/filterInputSignal/Unit Delay", "SampleTime"=>"-1")
engee.add_block("/Basic/Discrete/Unit Delay", "alphabetafilter/filterInputSignal/Unit Delay1") engee.set_param!("alphabetafilter/filterInputSignal/Unit Delay1", "InitialCondition"=>"0") engee.set_param!("alphabetafilter/filterInputSignal/Unit Delay1", "SampleTime"=>"-1")
engee.add_block("/Basic/Ports & Subsystems/Out1", "alphabetafilter/filterInputSignal/x") engee.set_param!("alphabetafilter/filterInputSignal/x", "InitialOutput"=>"0")
engee.add_line("alphabetafilter/filterInputSignal", "Add1/1", "Product/2") engee.add_line("alphabetafilter/filterInputSignal", "Add1/1", "Product1/1") engee.add_line("alphabetafilter/filterInputSignal", "Unit Delay/1", "Add/1") engee.add_line("alphabetafilter/filterInputSignal", "Add3/1", "Unit Delay1/1") engee.add_line("alphabetafilter/filterInputSignal", "Unit Delay1/1", "Add3/2") engee.add_line("alphabetafilter/filterInputSignal", "Unit Delay1/1", "Gain/1") engee.add_line("alphabetafilter/filterInputSignal", "y/1", "Add1/1") engee.add_line("alphabetafilter/filterInputSignal", "Product/1", "Add2/2") engee.add_line("alphabetafilter/filterInputSignal", "Add2/1", "Unit Delay/1") engee.add_line("alphabetafilter/filterInputSignal", "Add2/1", "x/1") engee.add_line("alphabetafilter/filterInputSignal", "Gain1/1", "Add3/1") engee.add_line("alphabetafilter/filterInputSignal", "Product1/1", "Gain1/1") engee.add_line("alphabetafilter/filterInputSignal", "alpha/1", "Product/1") engee.add_line("alphabetafilter/filterInputSignal", "Gain/1", "Add/2") engee.add_line("alphabetafilter/filterInputSignal", "Add/1", "Add2/1") engee.add_line("alphabetafilter/filterInputSignal", "Add/1", "Add1/2") engee.add_line("alphabetafilter/filterInputSignal", "beta/1", "Product1/2")
engee.add_block("/Basic/Ports & Subsystems/Out1", "alphabetafilter/x") engee.set_param!("alphabetafilter/x", "InitialOutput"=>"0")
engee.add_line("alphabetafilter", "measurementNoiseVariance/1", "determineAlphaBeta/2") engee.add_line("alphabetafilter", "processNoiseVariance/1", "determineAlphaBeta/1") engee.add_line("alphabetafilter", "determineAlphaBeta/1", "filterInputSignal/1") engee.add_line("alphabetafilter", "determineAlphaBeta/2", "filterInputSignal/2") engee.add_line("alphabetafilter", "y/1", "filterInputSignal/3") engee.add_line("alphabetafilter", "filterInputSignal/1", "x/1")
engee.save(model, "alphabetafilter.engee") engee.close() end

Построение модели Engee

Второй и завершающий шаг переноса модели Simulink в модель Engee – воспроизведение полученного скрипта:

In [ ]:
cd(joinpath(путь_примера,"engee"))        # Переходим в папку примера – в ней будет располагаться модель .engee
include(скриптJL)                         # Запускаем выполнение скрипта .jl для автоматического создания модели .engee

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

При помощи функций engee.load() и engee.open() программного управления мы можем убедиться, что построенная модель с подсистемами открываются, и, следовательно, они созданы корректно.

In [ ]:
# Функция для проверки модели и подсистем на открытие
function model_check(name::String, path::String, subsystem::String, delay::Int)     # Функция получает имя и путь модели, имя подсистемы, время ожидания
    model = engee.load(path; sync_gui=true)                                         # Загружаем модель
    sleep(delay)                                                                        # Ожидаем завершения синхронизации GUI
    engee.open(joinpath(name,subsystem); sync_gui=true)                             # Открываем подсистему 
    sleep(delay)                                                                        # Ожидаем завершения синхронизации GUI
    engee.save(model, name*".engee"; force=true, sync_gui=true)                     # Обновляем модель
    sleep(delay)                                                                        # Ожидаем завершения синхронизации GUI
                                                                                    # Закрываем модель
    try
        engee.close(model;sync_gui=true)
    catch e
        engee.close_all(;sync_gui=true)
    end
    sleep(delay)                                                                        # Ожидаем завершения синхронизации GUI
    return nothing                                                                  # Функция ничего не возвращает
end;

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

In [ ]:
подсистемы = ["filterInputSignal", "determineAlphaBeta"]        # Вектор с именами открываемых подсистем

for i in подсистемы                                             # Цикл по вектору
    model_check(имя_модели, модельENGEE, i, 5)                  # Циклический вызов функции проверки модели и подсистем
                                                                # время ожидания = 3 [сек]. При медленной синхронизации GUI
                                                                # рекомендуется увеличить время ожидания
end

В процессе выполнения ячейки в среде моделирования Engee открываются и закрываются вкладки модели и подсистем. Необходимо дождаться завершения выполнения всех команд управления моделью и GUI.

Далее перейдём к верификации автоматически построенной модели .engee.

Верификация полученной модели

Воспользуемся способом, описанным в примере интеграции кода на языке Си.

Исходные данные для верификации – результаты работы модели Simulink. Для получения результатов сгенерируем из неё код при помощи Simulink Embedded Coder.

In [ ]:
# Загружаем и устанавливаем библиотеку MATLAB, если она ещё не установлена
import Pkg; Pkg.add("MATLAB")
   Resolving package versions...
  No Changes to `~/.project/Project.toml`
  No Changes to `~/.project/Manifest.toml`
In [ ]:
cgModel = модельSLX                                     # Путь модели Simulink
cgPath = mkdir(joinpath(путь_примера,"slx","cg"));      # Создаём директорию для результатов генерации кода из модели Simulink

using MATLAB                                            # Подключаем библиотеку MATLAB

mat"""                                                  % Вставка кода на языке MATLAB
model = load_system($cgModel);                              % Загружаем модель Simulink в память 
path = $cgPath;                                             % Сохраняем путь к папке генерации кода

set_param(0, 'CacheFolder', path)                           % Определяем путь к папке кэша моделирования
set_param(0, 'CodeGenFolder', path)                         % Определяем путь к папке генерации кода

slbuild(model)                                              % Генерируем код из исходной модели Simulink

set_param(0, 'CacheFolder', '')                             % Сбрасываем путь к папке кэша моделирования
set_param(0, 'CodeGenFolder', '')                           % Сбрасываем путь к папке генерации кода
"""                                                     # Конец вставки кода
>> >> >> >> Model 'alphabetafilter' was exported from R2024b to R2023a. To find blocks that were removed during the export operation, <a href="matlab:slexportprevious.utils.displayReplacedBlocks('alphabetafilter')">click here</a>. Save the model to remove this notification.
>> >> >> >> >> >> ### Starting build procedure for: alphabetafilter
### Generating code and artifacts to 'Model specific' folder structure
### Generating code into build folder: /user/start/examples/project_management/slx_to_engee_conversion/slx/cg/alphabetafilter_ert_rtw
### Invoking Target Language Compiler on alphabetafilter.rtw
### Using System Target File: /matlab/rtw/c/ert/ert.tlc
### Loading TLC function libraries
.......
### Generating TLC interface API for custom data
.
### Initial pass through model to cache user defined code
### Caching model source code
....................................
### Writing header file alphabetafilter_types.h
### Writing header file alphabetafilter.h
### Writing header file rtwtypes.h
### Writing source file alphabetafilter.c
### Writing header file alphabetafilter_private.h
.
### Writing source file alphabetafilter_data.c
### Writing source file ert_main.c
### TLC code generation complete (took 13.524s).
[Warning: P-code file /matlab/toolbox/coder/simulinkcoder_core/addStandardInfo.p
is older than source code file
/matlab/toolbox/coder/simulinkcoder_core/addStandardInfo.m.
/matlab/toolbox/coder/simulinkcoder_core/addStandardInfo.p may be obsolete and
may need to be regenerated.
Type "help pcode" for information about generating P-code.] 
### Saving binary information cache.
### Using toolchain: GNU gcc/g++ | gmake (64-bit Linux)
### Creating '/user/start/examples/project_management/slx_to_engee_conversion/slx/cg/alphabetafilter_ert_rtw/alphabetafilter.mk' ...
### Building 'alphabetafilter': "/matlab/bin/glnxa64/gmake"  -f alphabetafilter.mk all
gcc -c -fwrapv -fPIC -O0 -msse2 -DCLASSIC_INTERFACE=0 -DALLOCATIONFCN=0 -DTERMFCN=1 -DONESTEPFCN=1 -DMAT_FILE=0 -DMULTI_INSTANCE_CODE=0 -DINTEGER_CODE=0 -DMT=0  -DTID01EQ=0 -DMODEL=alphabetafilter -DNUMST=1 -DNCSTATES=0 -DHAVESTDIO -DMODEL_HAS_DYNAMICALLY_LOADED_SFCNS=0 -I/user/start/examples/project_management/slx_to_engee_conversion/slx/cg -I/user/start/examples/project_management/slx_to_engee_conversion/slx/cg/alphabetafilter_ert_rtw -I/matlab/extern/include -I/matlab/simulink/include -I/matlab/rtw/c/src -I/matlab/rtw/c/src/ext_mode/common -I/matlab/rtw/c/ert -o "alphabetafilter.o" "/user/start/examples/project_management/slx_to_engee_conversion/slx/cg/alphabetafilter_ert_rtw/alphabetafilter.c"
gcc -c -fwrapv -fPIC -O0 -msse2 -DCLASSIC_INTERFACE=0 -DALLOCATIONFCN=0 -DTERMFCN=1 -DONESTEPFCN=1 -DMAT_FILE=0 -DMULTI_INSTANCE_CODE=0 -DINTEGER_CODE=0 -DMT=0  -DTID01EQ=0 -DMODEL=alphabetafilter -DNUMST=1 -DNCSTATES=0 -DHAVESTDIO -DMODEL_HAS_DYNAMICALLY_LOADED_SFCNS=0 -I/user/start/examples/project_management/slx_to_engee_conversion/slx/cg -I/user/start/examples/project_management/slx_to_engee_conversion/slx/cg/alphabetafilter_ert_rtw -I/matlab/extern/include -I/matlab/simulink/include -I/matlab/rtw/c/src -I/matlab/rtw/c/src/ext_mode/common -I/matlab/rtw/c/ert -o "alphabetafilter_data.o" "/user/start/examples/project_management/slx_to_engee_conversion/slx/cg/alphabetafilter_ert_rtw/alphabetafilter_data.c"
gcc -c -fwrapv -fPIC -O0 -msse2 -DCLASSIC_INTERFACE=0 -DALLOCATIONFCN=0 -DTERMFCN=1 -DONESTEPFCN=1 -DMAT_FILE=0 -DMULTI_INSTANCE_CODE=0 -DINTEGER_CODE=0 -DMT=0  -DTID01EQ=0 -DMODEL=alphabetafilter -DNUMST=1 -DNCSTATES=0 -DHAVESTDIO -DMODEL_HAS_DYNAMICALLY_LOADED_SFCNS=0 -I/user/start/examples/project_management/slx_to_engee_conversion/slx/cg -I/user/start/examples/project_management/slx_to_engee_conversion/slx/cg/alphabetafilter_ert_rtw -I/matlab/extern/include -I/matlab/simulink/include -I/matlab/rtw/c/src -I/matlab/rtw/c/src/ext_mode/common -I/matlab/rtw/c/ert -o "ert_main.o" "/user/start/examples/project_management/slx_to_engee_conversion/slx/cg/alphabetafilter_ert_rtw/ert_main.c"
### Creating standalone executable ../alphabetafilter ...
g++  -o ../alphabetafilter alphabetafilter.o alphabetafilter_data.o ert_main.o  
### Created: ../alphabetafilter
### Successfully generated all binary outputs.
gmake: Nothing to be done for `all'.
### Successful completion of build procedure for: alphabetafilter
### Simulink cache artifacts for 'alphabetafilter' were created in '/user/start/examples/project_management/slx_to_engee_conversion/slx/cg/alphabetafilter.slxc'.

Build Summary

Top model targets built:

Model            Action                        Rebuild Reason                                    
=================================================================================================
alphabetafilter  Code generated and compiled.  Code generation information file does not exist.  

1 of 1 models built (0 models already up to date)
Build duration: 0h 0m 39.355s

Модель, в которой при помощи блока C Function встраивается сгенерированный код из модели Simulink, – test_cg_simulink.engee. Необходимые пути к файлам Си уже указаны в блоке C Function.
Загрузим, выполним и закроем эту модель.

In [ ]:
тест_моделиSLX = engee.load(joinpath(путь_примера, "test_cg_simulink.engee"), force = true);    # Загружаем модель для сбора исходных данных верификации
результаты_моделиSLX = engee.run(тест_моделиSLX);                                               # Выполняем модель и сохраняем исходные данные
In [ ]:
engee.close(тест_моделиSLX, force = true);    

Для наглядности построим графики сигналов, полученных в результате моделирования:

In [ ]:
# Передадим результаты моделирования в отдельные переменные:
noisy_t_slx = результаты_моделиSLX["noisy"].time;
noisy_v_slx = результаты_моделиSLX["noisy"].value;
filtered_t_slx = результаты_моделиSLX["filtered"].time;
filtered_v_slx = результаты_моделиSLX["filtered"].value;

plot(noisy_t_slx, noisy_v_slx, label = "Исходный сигнал")                   # Строим график зашумленного сигнала
plot!(filtered_t_slx, filtered_v_slx, label = "Отфильтрованный сигнал")     # Строим график отфильтрованного сигнала (при помощи кода, сгенерированного из модели Simulink)
plot!(legend = :bottomright)                                                # Расположение легенды

title!("Результаты работы исходной модели Simulink")                        # Заголовок
xlabel!("Время, [с]")                                                       # Подпись оси X
ylabel!("Амплитуда")                                                        # Подпись оси Y
Out[0]:

Моделирование полученной модели Engee

Теперь перейдём к моделированию и получению результатов от модели Engee, построенной при помощи автоматически сгенерированного скрипта.
Определяем настраиваемые параметры модели (те же значения, что и в модели Simulink):

In [ ]:
# Настраиваемые параметры модели:
dt = 0.01;
Gain = 1/dt;

Генерируем код Си из автоматически построенной модели Engee:

In [ ]:
engee.generate_code(модельENGEE, путь_примера*"/engee/code")        # Автоматическая генерация кода Си из модели Engee
[ Info: Generated code and artifacts: /user/start/examples/project_management/slx_to_engee_conversion/engee/code

Идентичная предыдущей модель, в которой также при помощи блока C Function встраивается сгенерированный код из модели Engee, – test_cg_engee.engee. Необходимые пути к файлам Си уже указаны в блоке C Function.
Загрузим, выполним и закроем эту модель.

In [ ]:
тест_моделиENGEE = engee.load(joinpath(путь_примера, "test_cg_engee.engee"), force = true);     # Загружаем модель для сбора данных верификации модели Engee
результаты_моделиENGEE = engee.run(тест_моделиENGEE);                                           # Выполняем модель и сохраняем исходные данные
In [ ]:
engee.close(тест_моделиENGEE, force = true);   

Для наглядности построим графики сигналов, полученных в результате моделирования:

In [ ]:
# Передадим результаты моделирования в отдельные переменные:
noisy_t_engee = результаты_моделиENGEE["noisy"].time;
noisy_v_engee = результаты_моделиENGEE["noisy"].value;
filtered_t_engee = результаты_моделиENGEE["filtered"].time;
filtered_v_engee = результаты_моделиENGEE["filtered"].value;

plot(noisy_t_engee, noisy_v_engee, label = "Исходный сигнал")                   # Строим график зашумленного сигнала
plot!(filtered_t_engee, filtered_v_engee, label = "Отфильтрованный сигнал")     # Строим график отфильтрованного сигнала (при помощи кода, сгенерированного из модели Engee)
plot!(legend = :bottomright)                                                    # Расположение легенды

title!("Результаты работы построенной модели Engee")                            # Заголовок
xlabel!("Время, [с]")                                                           # Подпись оси X
ylabel!("Амплитуда")                                                            # Подпись оси Y
Out[0]:

Сравнение результатов моделирования

Графики позволяют уверенно сделать вывод, что результаты построения моделей в двух рассматриваемых нами средах моделирования аналогичны. Вычислим минимальные и максимальные значения абсолютных отклонений результатов моделирования для модели Simulink и сконвертированной из неё модели Engee:

In [ ]:
# Вычисляем абсолютные отклонения величин смоделированных сигналов
Δ_noisy_v = noisy_v_slx .- noisy_v_engee;
Δ_filtered_v = filtered_v_slx .- filtered_v_engee;

println("""Абсолютные отклонения величин сигналов для построенной модели Engee от исходной модели Simulink:
 - зашумленный сигнал       минимум: $(minimum(Δ_noisy_v))    максимум: $(maximum(Δ_noisy_v))
 - отфильтрованный сигнал   минимум: $(minimum(Δ_filtered_v))    максимум: $(maximum(Δ_filtered_v))""")
Абсолютные отклонения величин сигналов для построенной модели Engee от исходной модели Simulink:
- зашумленный сигнал       минимум: 0.0    максимум: 0.0
- отфильтрованный сигнал   минимум: 0.0    максимум: 0.0

Как видно из графического и аналитического сопоставления, в работе автоматически сгенерированной модели Engee нет отклонений по сравнению с работой исходной модели Simulink.

Выводы

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

In [ ]:
# rm(модельSLX; force=true); # При необходимости раскомментируйте строку

Блоки, использованные в примере