Engee Function
Использование кода Julia в моделях.
Тип: EngeeFunction
Путь в библиотеке:
|
Описание
Блок Engee Function позволяет использовать код на языке Julia в моделях Engee.
| Подробнее о языке программирования Julia читайте на странице Программирование. |
| В блоке Engee Function поддерживается большая часть возможностей языка Julia. При этом использование в блоке пакетного менеджера Pkg не предусматривается. |
Использование
Для интеграции кода на Julia в модель Engee необходимо:
-
Добавить в модель блок Engee Function из раздела Basic/User-Defined Functions библиотеки блоков
; -
В окне настроек
на вкладке Main блока Engee Function нажать на кнопку Редактировать исходный код для открытия редактора исходного кода (EngeeFunctionCode):
Ячейки исходного кода
Редактор исходного кода EngeeFunctionCode состоит из функциональных ячеек с кодом Julia. По умолчанию доступны три ячейки: вспомогательная (нередактируемая), Component struct code и Step method code (ячейки можно скрыть):

|
Для подключения дополнительных файлов исходного кода можно использовать функцию
|
| Весь код блока Engee Function можно написать в ячейке Common code, получив полный контроль над структурой компонента, сигнатурами и количеством функций. |
Для добавления/удаления других функциональных ячеек нажмите на кнопку «Управление методами»
и установите/уберите флажки на нужных ячейках:
→ 
Каждая ячейка отвечает за уникальный функционал блока Engee Function. Подробное описание каждой ячейки приведено ниже.
Информационная ячейка (нередактируемая)
Details
Автоматически отображает переменные блока Engee Function, атрибуты входных и выходных сигналов (размерность, тип, дискретность) и другие параметры, заданные пользователем. Ее содержимое обновляется динамически в зависимости от настроек блока. Ячейка всегда активна, но не выбирается в меню управления методами
. Содержит полупрозрачный текст с подсказками.
Код ячейки по умолчанию:
START_TIME = 0.0
STOP_TIME = 10
BLOCK_NAME = "Engee Function-1"
INPUT_SIGNAL_ATTRIBUTES[1] = (dimensions = -1, type = Inherit, sample_time = (period = -1.0, offset = 0.0), direct_feedthroughs = true)
OUTPUT_SIGNAL_ATTRIBUTES[1] = (dimensions = -1, type = Float64, sample_time = (period = -1.0, offset = 0.0))
gain = 2
const Dimensions = NTuple{N, Int} where N
const SampleTime = NamedTuple{(:period, :offset, :mode), Tuple{Rational{Int64}, Rational{Int64}, Symbol}}
const SAMPLE_TIME = (period = -1//1, offset = 0//1, mode = :Unknown)
| Изменение параметров блока влияет не только на содержимое информационной ячейки, но и на подсказки в других ячейках, которые также отображаются полупрозрачным текстом. |
Define component struct
Details
Добавляет ячейку Component struct code, в которой задается структура компонента блока Engee Function (наследуемая от типа AbstractCausalComponent). Поля структуры определяются между нередактируемыми строками struct Block <: AbstractCausalComponent и end. По умолчанию создается параметр g, инициализируемый значением блока gain, а конструктор Block() не принимает аргументов.
Код ячейки по умолчанию:
struct Block <: AbstractCausalComponent
# Для того, чтобы создать свой блок, определите структуру, наследуемую от
# `AbstractCausalComponent`. Конструктор не должен принимать аргументов.
# Параметр структуры `g` инициализируется значением параметра блока `gain`.
g::Real
function Block()
new(gain)
end
end
Use common code
Details
Добавляет ячейку Common code, в которой пишется код в свободной форме. По умолчанию ячейка пуста. Например, если стандартное объявление структуры в Component struct code не подходит (из-за нередактируемости struct Block <: AbstractCausalComponent), то можно отключить Define component struct, чтобы удалить ячейку, и определить структуру компонента вручную в Common code. То же самое касается любых функций из меню управления методами
— вместо стандартных ячеек можно писать свой код в Common code.

| Объявление компонента и функтора обязательно для работы Engee Function. Если Define component struct и Define step method отключены, то их код необходимо задать в Common code, иначе блок не будет работать. |
| Для переопределения функций наследования (Override, см. ниже) сначала нужно включить соответствующую ячейку, стереть ее содержимое, а затем написать новый код в Common code. |
Define step method
Details
Добавляет ячейку Step method code, в которой задается метод Step, вычисляющий выходные сигналы блока Engee Function. Сигнатура метода генерируется автоматически в зависимости от значений соответствующих меток (Label) портов на вкладке Ports. Метод представлен в виде функтора (подробнее см. здесь) и вызывается на каждом шаге симуляции. Поля определяются между нередактируемыми строками function (c::Block)(t::Real, in1) и end. Первый аргумент t::Real — время симуляции, далее передаются переменные входных сигналов. Вычисленные значения выходов возвращаются через ключевое слово return.
Код ячейки по умолчанию:
function (c::Block)(t::Real, in1)
# Функция для вычисления значений выходных сигналов блока.
# Первый аргумент `t` - время, остальные - значения входных сигналов.
# В данном примере выходом блока является результат умножения
# входного сигнала на внутренний параметр структуры `g`.
return c.g .* in1
end
Define update method
Details
Добавляет ячейку Update method code, в которой метод update! обновляет внутреннее состояние блока Engee Function на каждом шаге симуляции. Первый аргумент c::Block — структура блока, второй t::Real — время симуляции, далее передаются входные сигналы. Если блок не имеет внутреннего состояния, то метод может оставаться пустым и просто возвращать c.
Код ячейки по умолчанию:
function update!(c::Block, t::Real, in1)
# В случае, если блок имеет внутреннее состояние, необходимо определить метод
# `update!`. Первый аргумент - структура блока, второй - время, остальные - значения
# входных сигналов. В данном случае блок не имеет внутреннего состояния и
# метод не имеет эффектов.
return c
end
Если требуется определить несколько методов update! или задать метод с другой сигнатурой, то ячейку Update method code можно отключить и написать код в ячейке Common code. Компилятор автоматически определит наличие метода update! и использует его для симуляции.
|
Define terminate method code
Details
Добавляет ячейку Terminate method code, которая выполняется при завершении симуляции блока Engee Function (с помощью метода terminate!). Первый аргумент c::Block — структура блока. По умолчанию метод не выполняет дополнительных действий и просто возвращает c.
Код ячейки по умолчанию:
function terminate!(c::Block)
return c
end
Override type inheritance method
Details
Добавляет ячейку Types inheritance method, которая переопределяет метод наследования типов.
-
Если флажок Override type inheritance method снят (по умолчанию) — типы входных/выходных портов наследуются по правилам, указанным в описании входных/выходных портов.
-
Если флажок Override type inheritance method установлен — типы входных/выходных портов наследуются по правилам, указанным в функции
propagate_typesячейки Types inheritance method в исходном коде. Функцияpropagate_typesпринимает один аргумент — вектор типов, по одному типу на каждый входной сигнал, и возвращает вектор выходных типов.
Код ячейки по умолчанию:
function propagate_types(inputs_types::Vector{DataType})::Vector{DataType}
# Функция, возвращающая массив типов сигналов на выходе.
# Игнорируется, если используется алгоритм по умолчанию.
# В данном случае учитываются тип входного сигнала и тип
# параметра блока `gain`.
input_type = first(inputs_types)
# promote_type возвращает тип, к которому приводятся типы-аргументы
# при арифметических операциях с объектами этих типов.
output_type = promote_type(input_type, eltype(gain))
return [output_type]
end
Здесь для наследования типов берется общий элемент входного сигнала и элемент параметра, заданного в настройках блока на вкладке Parameters.
Override dimensions inheritance method
Details
Добавляет ячейку Dimensions inheritance method, которая переопределяет метод наследования размерностей.
-
Если флажок Override dimensions inheritance method снят (по умолчанию) — размерности входных/выходных портов наследуются по правилам, указанным в описании входных/выходных портов.
-
Если флажок Override dimensions inheritance method установлен — размерности входных/выходных портов наследуются по правилам, указанным в функции
propagate_dimensionsячейки Dimensions inheritance method в исходном коде. Функцияpropagate_dimensionsпринимает массив кортежей (размерностей) на каждый входной сигнал и возвращает массив размерностей на выходе.
Код ячейки по умолчанию:
function propagate_dimensions(inputs_dimensions::Vector{<:Dimensions})::Vector{<:Dimensions}
# Функция, возвращающая массив размерностей сигналов на выходе.
# Игнорируется, если используется алгоритм по умолчанию.
# В данном случае учитываются размерности входного сигнала и
# параметра блока `gain`.
input_dimensions = first(inputs_dimensions)
mock_input = zeros(input_dimensions)
mock_output = mock_input .* gain
return [size(mock_output)]
end
Здесь для наследования размерностей берется массив с нужными размерностями (mock_input), состоящий из нулей. Он умножается на элемент параметра, заданного в настройках блока на вкладке Parameters, после чего берется его размерность.
Use common method for types and dimensions inheritance
Details
Добавляет ячейку Common types and dimensions inheritance method, которая использует общий метод для переопределения наследования типов и размерностей одновременно. В отличие от конкретных методов для типов (Types inheritance method) или размерностей (Dimensions inheritance method), общий метод включает в себя и типы, и размерности одновременно:
-
Если флажок Use common method for types and dimensions inheritance снят (по умолчанию) — общий метод игнорируется, размерности и типы входных/выходных портов наследуются по правилам, указанным в описании входных/выходных портов или в методах наследования Override type inheritance method (если включено) и Override dimensions inheritance method (если включено).
-
Если флажок Use common method for types and dimensions inheritance установлен — размерности и типы входных/выходных портов наследуются по правилам, указанным в функции
propagate_types_and_dimensionsячейки Common types and dimensions inheritance method в исходном коде.
Код ячейки по умолчанию:
function propagate_types_and_dimensions(inputs_types::Vector{DataType}, inputs_dimensions::Vector{<:Dimensions})::Tuple{Vector{DataType}, Vector{<:Dimensions}}
# Функция, возвращающая массив типов сигналов и массив
# размерностей сигналов на выходе. Эту функцию можно использовать
# если необходимо одновременно переопределить и алгоритм наследования
# типов сигналов, и алгоритм наследования размерностей.
outputs_types = propagate_types(inputs_types)
outputs_dimensions = propagate_dimensions(inputs_dimensions)
return outputs_types, outputs_dimensions
end
Зависимости
Чтобы использовать эту ячейку, установите флажки методов Override type inheritance method и Override dimensions inheritance method.
Override sample time inheritance method
Details
Добавляет ячейку Sample times inheritance method, которая переопределяет метод наследования шага расчета.
Код структуры шага расчета SampleTime и функции propagate_sample_times не будут автоматически добавлены в исходный код EngeeFunctionCode старых моделей Engee. Для доработки старых моделей добавьте структуру шага расчета и функцию.
|
-
Если флажок Override sample time inheritance method снят (по умолчанию) — используется предустановленный способ наследования шага расчета (по умолчанию
Default) из параметра Sample time inheritance method вкладки Advanced. Подробнее о предустановленных способах читайте в описании параметра Sample time inheritance method. -
Если флажок Override sample time inheritance method установлен — предустановленные способы вкладки Advanced игнорируются (параметр недоступен), используется метод наследования из ячейки Sample times inheritance method в исходном коде EngeeFunctionCode.
Для работы метода наследования из ячейки необходимо найти строку функции propagate_sample_times и вручную задать нужный шаг расчета.
Код ячейки по умолчанию:
function propagate_sample_times(inputs_sample_times::Vector{SampleTime}, fixed_solver::Bool)::SampleTime
# Функция, возвращающая время дискретизации блока.
# Используется только в режиме наследования `Custom`.
# Параметр fixed_solver говорит о том, используется решатель
# с постоянным шагом (true) или с переменным (false).
# Более сложный пример работы с наследованием времени
# дискретизации блока можно посмотреть в документации.
return first(inputs_sample_times)
end
где шаг расчета имеет вид:
const SampleTime = NamedTuple{(:period, :offset, :mode), Tuple{Rational{Int64}, Rational{Int64}, Symbol}}
Override direct feedthrough setting method
Details
Добавляет ячейку Direct feedthrough setting method, которая определяет прямое сквозное соединение.
-
Если флажок Override direct feedthrough setting method снят (по умолчанию), то прямое сквозное соединение определяется значением параметра Direct feedthrough.
-
Если флажок Override direct feedthrough setting method установлен, то доступно прямое сквозное соединение. Это означает, что выходной сигнал контролируется непосредственно значением входного порта.
Код ячейки по умолчанию:
function direct_feedthroughs()::Vector{Bool}
# Функция, возвращающая массив булевых значений, определяющих,
# сквозные соединения. Если i-ый элемент массива равен true,
# то i-ый порт имеет сквозное соединение.
# Игнорируется, если используется алгоритм по умолчанию.
return [true]
end
Пример:
function direct_feedthroughs()::Vector{Bool}
if gain == 2
return [false]
else
return [true]
end
end
Константы и функции получения атрибутов
Для того чтобы узнать типы, размеры и другую вспомогательную информацию в исполняемом коде блока, используйте следующие константы в коде Engee Function:
-
BLOCK_NAME— имя блока. У каждого блока, добавленного на холст Engee, есть имя, по которому можно обратиться через эту константу. Например, можно обратиться кBLOCK_NAMEво время инициализации ошибки, чтобы вывести в ней имя блока. -
START_TIME— начало симуляции из настроек модели. -
STOP_TIME— конец симуляции из настроек модели. -
INPUT_SIGNAL_ATTRIBUTES— списки из атрибутов по каждому входному порту. Например, чтобы узнать атрибуты первого входного сигнала, используйтеINPUT_SIGNAL_ATTRIBUTES[1], где1— первый входной порт блока Engee Function. -
OUTPUT_SIGNAL_ATTRIBUTES— списки из атрибутов по каждому выходному порту. Например, чтобы узнать атрибуты первого выходного сигнала, используйтеOUTPUT_SIGNAL_ATTRIBUTES[1], где1— первый выходной порт блока Engee Function.
Переменные START_TIME и STOP_TIME доступны во всех ячейках исходного кода Engee Function (включая методы наследования override). Они содержат время начала и окончания симуляции, заданное в настройках модели. Частые сценарии использования:
-
Вычисление длительности симуляции
STOP_TIME - START_TIME. -
Выполнение действия на первом/последнем шаге (инициализация/финализация).
-
Задание дискретного шага как доли общей длительности (например, чтобы получить
Nточек на интервале).
Чтобы узнать дополнительную информацию о конкретном порте блока, можно обратиться к атрибутам его сигнала, добавив точку . после константы INPUT_SIGNAL_ATTRIBUTES[i], где [i] — номер входного порта, и OUTPUT_SIGNAL_ATTRIBUTES[i], где [i] — номер выходного порта соответственно. Дополнительную информацию можно получить через следующие функции обращения:
-
dimensions— размерность сигнала. Можно сократить доdims. -
type— тип сигнала. Можно сократить доtp. -
sample_time— шаг расчета. Представляет собой структуру по аналогии с атрибутами сигналов, к которой можно обращаться через точку.. Доступны две функции обращения:-
period— период шага расчета. Полная функция обращения —sample_time.period. Можно сократить доst.p. -
offset— смещение шага расчета. Полная функция обращения —sample_time.offset. Можно сократить доst.o.
-
-
direct_feedthrough— определяет, имеет ли порт прямое сквозное соединение. Используется только для входных портов (проверяет атрибуты только для входного порта). Можно сократить доdf.
Пример модели со всеми константами и функциями обращения
Details
Пример модели с Engee Function со всеми константами и функциями обращения:

struct Block <: AbstractCausalComponent end
function (c::Block)(t::Real, x1, x2)
y1 = [START_TIME, STOP_TIME]
y2 = collect(INPUT_SIGNAL_ATTRIBUTES[1].dimensions)
y3 = OUTPUT_SIGNAL_ATTRIBUTES[1].dims[1]
y4 = (INPUT_SIGNAL_ATTRIBUTES[2].type == Int64)
y5 = (OUTPUT_SIGNAL_ATTRIBUTES[4].tp == Bool)
y6 = INPUT_SIGNAL_ATTRIBUTES[1].sample_time.period
y7 = OUTPUT_SIGNAL_ATTRIBUTES[1].st.p
y8 = INPUT_SIGNAL_ATTRIBUTES[1].sample_time.offset
y9 = OUTPUT_SIGNAL_ATTRIBUTES[2].st.o
y10 = INPUT_SIGNAL_ATTRIBUTES[1].direct_feedthrough
y11 = INPUT_SIGNAL_ATTRIBUTES[2].df
return (y1, y2, y3, y4, y5, y6, y7, y8, y9, y10, y11)
end
Использование параметров как переменных
Глобальные переменные, доступные в окне переменных Engee
, можно задавать на вкладке Parameters для вставки в исходный код блока Engee Function. Если имя параметра и глобальной переменной совпадают, то значение параметра будет автоматически подставляться из глобальной переменной.
|
Важно отличать параметры блока (задаются на вкладке Parameters и являются глобальными переменными в исходном коде Engee Function) от глобальных переменных Engee в окне переменных |
Пример использования параметров как переменных
Details
Рассмотрим случай, когда глобальные переменные полностью соответствуют переменным в исходном коде. Например, были заданы три глобальные переменные a, b, c со значениями 1, 2, 3 соответственно. Все три глобальные переменные используются в качестве параметров блока Engee Function:

Тогда исходный код с добавлением параметров будет выглядеть так:
struct Block <: AbstractCausalComponent
a::Real
b::Real
c::Real
function Block()
new(a_param, b_param, c_param)
end
end
function (c::Block)(t::Real, x::Vector{<:Real})
return (c.a .* x .+ c.b) ./ c.c
end
Этот код определяет структуру Block, которая настраивает поведение компонента. В примере используются имена параметров блока a_param, b_param и c_param, чтобы задать параметры структуры a, b и c соответственно. В коде также определяется метод function(c::Block)(t::Real, x::Vector{<:Real}), который масштабирует каждый элемент вектора x на параметр блока a, добавляет b и делит результат на c. Это позволяет гибко изменять и нормализовать вектор x в соответствии со значениями параметров блока.
Рассмотрим случай, когда используются только параметры вкладки Parameters:
struct Block <: AbstractCausalComponent end
function (c::Block)(t::Real, x::Vector{<:Real})
return (a_param .* x .+ b_param) ./ c_param
end
Эти параметры будут глобальными переменными в коде блока. Это означает, что они всегда будут доступны в любой части кода блока без необходимости определять их повторно в каждой функции или блоке кода. Это значительно упрощает код и позволяет легко изменять параметры на вкладке Parameters, не затрагивая исходный код.
Рассмотрим случай, когда параметры не полностью соответствуют исходному коду. Например, есть параметр a_param, равный 100. В исходном коде есть поле структуры a:
struct Block <: AbstractCausalComponent
a::Real
function Block()
new(a_param/10)
end
end
function (c::Block)(t::Real, x::Vector{<:Real})
c.a
end
В этом коде параметр a_param используется для инициализации поля a структуры struct Block через ее конструктор, который делит значение параметра на 10. В данном случае в функторе блока возвращается поле a.
Переменные можно сделать глобальными прямо в исходном коде:
a = 1;
b = 2;
с = 3;
struct Block <: AbstractCausalComponent; end
function (c::Block)(t::Real, x::Vector{<:Real})
return (a .* x .+ b) ./ c
end
В коде создается структура Block и определяется функция, которая выполняет математические операции с глобальными переменными a, b и c, применяя их к переменной x.
Если вы не хотите использовать параметры из вкладки Parameters, то можно инициализировать переменные прямо в исходном коде:
struct Block <: AbstractCausalComponent; end
function (c::Block)(t::Real, x)
a = 1;
b = 2;
c = 3;
return (a .* x .+ b) ./ c
end
В коде создается структура Block, определяющая функцию, которая выполняет математические операции с локальными переменными a, b и c, применяя их к переменной x.
Рассмотрим наиболее эффективный и правильный способ выполнения. Чтобы блок Engee Function работал корректно, установите флажок для опции Use external cache for non-scalar output на вкладке Main блока Engee Function. Исходный код будет выглядеть так:
struct Block{Ta, Tb, Tc} <: AbstractCausalComponent
a::Ta
b::Tb
c::Tc
function Block()
Ta = typeof(a_param); Tb = typeof(b_param); Tc = typeof(c_param)
all(isreal, (a_param, b_param, c_param)) ||
error("Параметры блока должны быть вещественными")
all(x->isempty(size(x)), (a_param, b_param, c_param)) ||
error("Параметры блока должны быть скалярами")
new{Ta, Tb, Tc}(a_param, b_param, c_param)
end
end
function (c::Block)(t::Real, cache::Vector{<:Real}, x::Vector{<:Real})
cache .= (c.a .* x .+ c.b) ./ c.c
nothing
end
| Подобный код можно написать только в ячейке Common code, так как стандартные ячейки не позволяют редактировать определение структуры компонента и сигнатуру функтора. |
Поля a, b и c структуры представляют собой параметры блока, которые строго проверяются на типы в конструкторе. Каждый из этих параметров должен быть вещественным скаляром (Real), что обеспечивает точность вычислений во время выполнения.
Конструктор Block() проверяет типы переданных параметров , и . Если хотя бы один из них не является вещественным скаляром или имеет несоответствующие размерности (должны быть скалярами), конструктор генерирует ошибку с соответствующим сообщением. После проверки конструктор инициализирует поля структуры значениями , и заданных типов Ta, Tb и Tc.
Вычисляемая функция, определенная для экземпляров Block, принимает время t, внешний кэш и вектор x вещественных чисел. В этой функции используются поля a, b и c структуры Block для вычисления значений, которые затем записываются в cache. Это позволяет избежать лишних выделений памяти путем повторного использования предоставленного кэша.
Таким образом, структура Block обеспечивает строгое управление типами данных и эффективное использование ресурсов благодаря использованию внешнего кэша для хранения результатов вычислений.
Если вы хотите менять параметры блока, то необходимо использовать mutable структуру. Рассмотрим пример:
mutable struct Counter{T} <: AbstractCausalComponent
limit::T
iter::T
function Counter()
isempty(size(limit)) || error("Предел блока $BLOCK_NAME должен быть скаляром")
isreal(limit) || error("Предел блока $BLOCK_NAME должен быть вещественным числом")
T = typeof(limit)
iter = zero(T)
new{T}(limit, iter)
end
end
function (c::Counter)(t::Real)
return c.iter
end
function update!(c::Counter, t::Real)
c.iter += 1
if c.iter > c.limit
c.iter = zero(c.iter)
end
c
end
Структура Counter — это mutable тип данных для подсчета итераций с заданным пределом. Она является строго типизированной. Поля limit и iter структуры представляют собой параметры блока:
-
limit— это предельное значение счетчика; -
iter— текущее значение счетчика.
В конструкторе структуры выполняется проверка параметра limit — он должен быть скаляром и иметь вещественный тип данных. После этого инициализируется поле iter с нулевым значением соответствующего типа T. Функция update! обновляет состояние счетчика c, увеличивая значение iter на единицу при каждом вызове. Если текущее значение iter превышает limit, то оно обнуляется, что позволяет счетчику циклически возвращаться к начальному состоянию.
В исходном коде блока Engee Function можно использовать include, ссылающийся на внешний код. Это позволяет инициализировать переменные из внешнего кода (при их наличии) в исходном коде.
|
| Фактический тип данных, как и поддержка возможных типов данных, зависит от пользовательского кода внутри блока. |
Порты
Вход
Input Port —
входной порт
скаляр | вектор | матрица | массив
Details
Входной сигнал, заданный в виде скаляра, вектора, матрицы или массива.
Количество входных портов задается в параметре Number of input ports.
Для каждого входного порта можно настроить параметры на вкладке Ports. Подробнее см. в разделе Input port.
Выход
Output port —
выходной порт
скаляр | вектор | матрица | массив
Details
Выходной сигнал, возвращаемый в виде скаляра, вектора, матрицы или массива.
Количество выходных портов задается в параметре Number of output ports.
Для каждого выходного порта можно настроить параметры на вкладке Ports. Подробнее см. в разделе Output port.
Параметры
Main
Number of input ports —
количество входных портов
Integer
Details
Укажите количество входных портов блока.
| Значение по умолчанию |
|
| Имя для программного использования |
|
| Настраиваемый |
Нет |
| Вычисляемый |
Да |
Number of output ports —
количество выходных портов
Integer
Details
Укажите количество выходных портов блока.
| Значение по умолчанию |
|
| Имя для программного использования |
|
| Настраиваемый |
Нет |
| Вычисляемый |
Да |
Sample time —
интервал между шагами расчета
SampleTime (real number / vector of two real numbers)
Details
Укажите интервал между шагами расчета как неотрицательное число. Чтобы наследовать шаг расчета, установите для этого параметра значение −1.
| Значение по умолчанию |
|
| Имя для программного использования |
|
| Настраиваемый |
Нет |
| Вычисляемый |
Да |
Parameters
Number of parameters —
число параметров
Integer
Details
Укажите число параметров, используемых в блоке.
| Значение по умолчанию |
|
| Имя для программного использования |
|
| Настраиваемый |
Нет |
| Вычисляемый |
Да |
Name —
имя параметра
String
Details
Укажите имя параметра для использования в исходном коде блока Engee Function.
Имя первого параметра (присутствующего по умолчанию) — gain, значение которого равно 2. Новые параметры называются parameter2 и далее по возрастанию. Значение новых параметров по умолчанию равно 0.
Подробнее см. в разделе Использование параметров как переменных.
| Значение по умолчанию |
|
| Имя для программного использования |
|
| Настраиваемый |
Нет |
| Вычисляемый |
Нет |
Value —
значение параметра
Arbitrary type
Details
Укажите значение параметра с именем Name. В качестве значений можно задавать любые выражения на языке Julia.
Чтобы задать параметр шины, задайте для параметра Value значение шины в виде именованного кортежа, например (s1 = 5, s2 = 4).
Подробнее см. в разделе Использование параметров как переменных.
Advanced
Use external cache for non-scalar output —
внешний кэш для нескалярных выходных сигналов
Logical
Details
Установите флажок, чтобы использовать внешний кэш для нескалярного (многомерного) выходного сигнала для экономии оперативной памяти Engee. Используется, если у блока Engee Function только один выходной порт:
-
Если флажок снят (по умолчанию) — внешний кэш не используется.
-
Если флажок установлен — выходной сигнал сможет принимать дополнительный аргумент
cache, который необходимо учесть в исходном коде EngeeFunctionCode. Если у блока один выходной порт, то в ячейке Step method code аргументcacheавтоматически добавляется в список аргументов функции, кроме случая, когда размерность порта явно задана как()(скаляр). В исходном коде необходимо определить функторы в зависимости от размерности выходного сигнала. Функторы можно определить в ячейке Common code:-
Если выходной сигнал скалярный:
function (c::Block)(t::Real, x) return c.g * x end -
Если выходной сигнал нескалярный:
function (c::Block)(t::Real, cache, x) cache .= c.g .* x nothing endгде
t— время,x— аргумент (информация с входных портов). Параметр времени должен быть указан, даже если он отсутствует в параметрах блока.
-
| Значение по умолчанию |
|
| Имя для программного использования |
|
| Настраиваемый |
Нет |
| Вычисляемый |
Нет |
Sample time inheritance method —
метод наследования шага расчета
Default | Discrete | Continuous
Details
| Настройка Sample time inheritance method пропадает с вкладки Advanced, если в исходном коде EngeeFunctionCode установлен флажок Override sample time inheritance method. В этом случае метод наследования шага расчета определяется кодом функциональной ячейки Sample times inheritance method. |
Определяет метод наследования шага расчета в зависимости от выбранного значения:
-
Default— способ наследования шага расчета по умолчанию. Способ наследованияDefaultиспользуется, когда блок Engee Function не является дискретным или непрерывным. Он допускает любой вид шага расчета. При выборе этого способа блок Engee Function будет наследовать шаг расчета по следующим принципам:-
Если у блока нет входных портов — на выходе непрерывный шаг расчета.
-
Если на входе все шаги расчета одинаковы — на выходе шаг расчета совпадает со входом.
-
Если среди входных шагов расчета есть непрерывные — на выходе тоже есть непрерывные шаги расчета.
-
Если среди входных шагов расчета есть фиксированный малый (FiM, Fixed-in-Minor), нет непрерывного шага расчета и решатель с переменным шагом — на выходе фиксированный малый.
-
Если на входе нет непрерывного и фиксированного малого шага расчета и не все шаги расчета равны — рассматриваются только дискретные шаги расчета на входе, для которых справедлив один из вариантов:
-
Если наибольший общий делитель дискретных шагов расчета совпадает с одним из входных шагов расчета или используется решатель с постоянным шагом — на выходе дискретный шаг расчета с шагом наибольшего общего делителя.
-
Если решатель с переменным шагом и наибольший общий делитель входных дискретных шагов расчета не совпадают ни с одним из входных шагов расчета — на выходе фиксированный малый.
-
-
-
Discrete— способ наследования для получения дискретного шага расчета. При выборе этого способа блок Engee Function будет наследовать шаг расчета по следующим принципам:-
Если на входе есть непрерывный или фиксированный малый шаг расчета — на выходе дискретный шаг расчета с шагом решателя (даже если решатель с переменным шагом).
-
Если на входе среди шагов расчета есть дискретные — на выходе дискретный шаг расчета с наибольшим общим делителем от входных дискретных шагов расчета.
-
-
Continuous— способ наследования для получения непрерывного шага расчета независимо от входных шагов расчета.
Если в поле Sample time указано значение −1, то шаг расчета наследуется согласно методу, указанному в поле Sample time inheritance method в зависимости от выбранного значения (Default, Discrete, Continuous). В остальных случаях (Sample time не равно −1 и Sample time больше или равно 0) — блок Engee Function работает с заданным значением поля Sample time, игнорируя Sample time inheritance method.
|
Пример переопределения функции propagate_sample_times с работой, аналогичной способу наследования Default:
function propagate_sample_times(inputs_sample_times::Vector{SampleTime}, fixed_solver::Bool)::SampleTime
nonnegative_sample_times = filter(
st -> st.period >= 0,
collect(values(inputs_sample_times)),
)
finite_periods = filter(
st -> !isinf(st.period),
nonnegative_sample_times,
) .|> (st -> st.period)
output_sample_time = if !isempty(nonnegative_sample_times)
if allequal(nonnegative_sample_times)
first(nonnegative_sample_times)
elseif any(st -> st.period == 0 // 1 && st.mode == :Continuous, nonnegative_sample_times)
(period = 0 // 1, offset = 0 // 1, mode = :Continuous)
elseif any(st -> st.mode == :FiM, nonnegative_sample_times) && !fixed_solver
(period = 0 // 1, offset = 0 // 1, mode = :FiM)
elseif (
all(x -> x.period > 0 // 1, nonnegative_sample_times) &&
(fixed_solver || gcd(finite_periods) in finite_periods)
)
(period = gcd(finite_periods), offset = 0 // 1, mode = :Discrete)
else
(period = 0 // 1, offset = 0 // 1, mode = :FiM)
end
else
(period = 0 // 1, offset = 0 // 1, mode = :Continuous)
end
return output_sample_time
end
Если функция propagate_sample_times возвращает (period = 0 // 1, offset = 0 // 1, mode = :Discrete), то такой шаг расчета будет воспринят как дискретный с шагом решателя.
|
| Значения |
|
| Значение по умолчанию |
|
| Имя для программного использования |
|
| Настраиваемый |
Нет |
| Вычисляемый |
Нет |
Ports
Input port
Label —
имя входного порта
String
Details
Укажите имя входного порта.
| Значение по умолчанию |
|
| Имя для программного использования |
|
| Настраиваемый |
Нет |
| Вычисляемый |
Нет |
Type —
тип входных данных
Inherit | Float64 | Float32 | Float16 | ComplexF64 | ComplexF32 | Bool | Int8 | UInt8 | Int16 | UInt16 | Int32 | UInt32 | Int64 | UInt64 | Int128 | UInt128 | BusSignal
Details
Выберите один из вариантов:
-
Определенный тип (все, кроме
Inherit) — проверяется, что на входной порт подается сигнал определенного типа. Выберите конкретный тип данных для входного порта. -
Inherit(по умолчанию) — наследует тип данных от связанного блока. Может быть любым типом данных.
| Значения |
|
| Значение по умолчанию |
|
| Имя для программного использования |
|
| Настраиваемый |
Нет |
| Вычисляемый |
Нет |
Output bus type —
тип входной шины
Data type
Details
Тип входной шины. Чтобы блок Engee Function понял, какая шина придет на вход, достаточно установить Type в значение Inherit. Явное указание типа необходимо только для выходного сигнала.
| Блок Engee Function не наследует шины на выходные порты, хотя и может получать их на входы. Для наследования шин на выходные порты (для передачи в другие блоки) нужно явно задать тип шины в параметре Output bus type. |
Зависимости
Чтобы использовать этот параметр, установите для параметра Type значение BusSignal.
| Значение по умолчанию |
|
| Имя для программного использования |
|
| Настраиваемый |
Нет |
| Вычисляемый |
Да |
Size —
размерность входного сигнала
Int64 or tuple of Int64
Details
Размерность входного сигнала может быть задана как:
-
−1(по умолчанию) — наследует размерность поданного на входной порт сигнала (сигнал может иметь любую размерность). -
Целое число или кортеж целых чисел — входной сигнал должен иметь заданное количество элементов. Размерности указываются в Julia-нотации, например,
()для скаляра,(2,)для одномерного сигнала из двух элементов или(2, 3, 4)для многомерного. -
Кортеж целых чисел и
−1— наследование некоторых размерностей. Например,(-1, 2)— первая размерность наследуется, а вторая задается явно.
| Значение по умолчанию |
|
| Имя для программного использования |
|
| Настраиваемый |
Нет |
| Вычисляемый |
Да |
Direct feedthrough —
прямое сквозное соединение
Logical
Details
Определяет прямое сквозное соединение:
-
Если флажок установлен (по умолчанию), то доступно прямое сквозное соединение. Это означает, что выходной сигнал контролируется непосредственно значением входного порта.
-
Если флажок снят, то прямое сквозное соединение недоступно. Это означает, что выходной сигнал не будет контролироваться значением входного порта, что позволяет блоку размыкать петли.
| Значение по умолчанию |
|
| Имя для программного использования |
|
| Настраиваемый |
Нет |
| Вычисляемый |
Нет |
Output port
Label —
Имя выходного порта
String
Details
Укажите имя выходного порта.
| Значение по умолчанию |
|
| Имя для программного использования |
|
| Настраиваемый |
Нет |
| Вычисляемый |
Нет |
Type —
тип выходных данных
Inherit | Float64 | Float32 | Float16 | ComplexF64 | ComplexF32 | Bool | Int8 | UInt8 | Int16 | UInt16 | Int32 | UInt32 | Int64 | UInt64 | Int128 | UInt128 | BusSignal
Details
Выберите один из вариантов:
-
Определенный тип (все, кроме
Inherit) — конкретный тип данных для выходного сигнала. -
Inherit(по умолчанию) — наследует тип данных выходного сигнала. Вычисляет наименьший общий тип при наличии нескольких входных сигналов разного типа. Может быть любым типом данных.
| Значения |
|
| Значение по умолчанию |
|
| Имя для программного использования |
|
| Настраиваемый |
Нет |
| Вычисляемый |
Нет |
Output bus type —
тип выходной шины
Data type
Details
Тип выходной шины. Чтобы блок Engee Function выдавал шину на выход, необходимо явно указать ее тип.
Зависимости
Чтобы использовать этот параметр, установите для параметра Type значение BusSignal.
| Значение по умолчанию |
|
| Имя для программного использования |
|
| Настраиваемый |
Нет |
| Вычисляемый |
Да |
Size —
размерность выходного сигнала
Int64 or tuple of Int64
Details
Размерность выходного сигнала может быть задана как:
-
−1(по умолчанию) — наследует размерность сигнала, поданного на входной порт. Если входных сигналов несколько, то наследование размерности подчиняется правилам broadcast механизма Julia. Частные случаи:-
если входные сигналы имеют одинаковую размерность, то выходной порт наследует ее;
-
если входные сигналы представляют собой скаляры и нескаляры (векторы, матрицы или многомерные массивы), которые имеют одну и ту же размерность, то наследуется именно она;
-
если к входным сигналам нельзя применить broadcast (например, входные сигналы — это векторы разной длины), то автоматическое наследование размерности невозможно. В этом случае потребуется явно задать размерность выходного сигнала.
-
-
Целое число или кортеж целых чисел — выходной сигнал должен иметь заданное количество элементов. Размерности указываются в Julia-нотации, например,
()для скаляра,(2,)для одномерного сигнала из двух элементов или(2, 3, 4)для многомерного. -
Кортеж целых чисел и
−1— наследование некоторых размерностей. Например,(-1, 2)— первая размерность наследуется, а вторая задается явно.
| Значение по умолчанию |
|
| Имя для программного использования |
|
| Настраиваемый |
Нет |
| Вычисляемый |
Да |
Работа с фиксированной точкой и пользовательскими шинами
Блок Engee Function, а также командная строка
Engee поддерживают работу с фиксированной точкой (Fixed) и пользовательскими типами шин (BusSignal). Эти конструкции помогают управлять поведением операций при работе с целыми, вещественными, фиксированными и комплексными типами данных.
Типы и функции фиксированной точки (Fixed-Point)
Details
В статье Арифметика с фиксированной точкой (Fixed-Point) в Engee описывается работа с фиксированной точкой в Engee. Приведенные в статье конструкции справедливы и для блока Engee Function, так в блоке поддерживаются:
-
FixedPoint— абстрактный тип всех фиксированных чисел; -
Fixed— конкретный тип фиксированного числа, который создается вручную с указанием битового представления; -
fi(…)— создание значения типаFixedс помощью числового значения и параметров представления; -
fixdt(…)— создание типа Fixed с указанием знака, ширины и количества дробных битов.
Например:
a = fi(5, 1, 16, 4) # Знаковое число, 16 бит, 4 дробных
b = fi(10, 0, 8, 0) # Беззнаковое целое 8 бит
T = fixdt(1, 24, 8) # Тип Fixed с 24 битами, 8 из них — дробные
c = Fixed(0x01ff, T) # Создание фиксированного числа напрямую по битовому представлению
Числа с фиксированной точкой в Engee Function можно:
-
передавать как параметры;
-
использовать в структуре компонента (ячейка Component struct code);
-
выполнять с ними арифметические операции через
emul,esum,edivв ячейке Step method code (подробнее см. в разделе Функции преобразования и типизированные арифметические операции:econvert,esum,emul,ediv); -
Применять при расчетах в
propagate_types(…),propagate_dimensions(…).
Работа с пользовательскими типами шин (BusSignal)
Details
Пользовательские типы шин позволяют задать типы входных и выходных сигналов в виде структурированных кортежей (NamedTuple) с описанием имен, типов и размерностей внутри Engee Function. Доступные функции:
-
BusSignal{Names, Types, Dimensions, :BusName}— шина с именами сигналов (Names), базовыми типами (BaseTypes), размерностями (Dims) и именем шины (BusName); -
get_bus_names(type)— получить список имен сигналов; -
get_bus_types(type)— получить типы сигналов; -
get_bus_dimensions(type)— получить размерности сигналов; -
get_names_types_dims(type)— получить все сразу; -
get_bus_signal_type(::NamedTuple)— определить типBusSignalпо значению.
Пример определения и анализа типа шины:
bus_type = BusSignal{(:s1, :s2, :s3), Tuple{Int64, Float64, Int8}, ((), (2,), (2, 2)), :MyBus}
get_names_types_dims(bus_type) # => ((:s1, :s2, :s3), (Int64, Float64, Int8), ((), (2,), (2, 2)))
get_bus_names(bus_type) # => (:s1, :s2, :s3)
get_bus_types(bus_type) # => (Int64, Float64, Int8)
get_bus_dimensions(bus_type) # => ((), (2,), (2, 2))
Можно явно описывать шины:
bus_obj = BusSignal{(:a, :b), Tuple{Int, Float64}, ((), (3,)), :MyBus}((a=1, b=[1.0, 2.0, 3.0]))
Также поддерживаются вложенные шины:
Inner = BusSignal{(:x, :y), Tuple{Int, Int}, ((), ()), :InnerBus}
Outer = BusSignal{(:a, :b), Tuple{Float64, Inner}, ((), ()), :OuterBus}
Использование в блоке Engee Function
Details
Типы Fixed и BusSignal можно использовать в различных частях блока Engee Function:
-
В параметрах блока — можно задать значения фиксированной точки через
fi(…), а также передать структуры шиныBusSignal{…}. -
В ячейке Common code — можно определить типы данных (
Fixed,BusSignal{…}), структуры компонента (struct Block), создать вспомогательные функции или инициализировать значения. Также сюда можно перенести основную логику, если отключены Step method code или Component struct code. -
В ячейке Component struct code — при описании полей структуры можно использовать значения типа
Fixed, а также типыBusSignal, если поля представляют собой составные сигналы. -
В ячейке Step method code — реализуется основная логика блока. Здесь фиксированные числа и сигналы шин могут участвовать в вычислениях, сравнениях и обработке структур.
-
В ячейке Types inheritance method — можно использовать типы
FixedиBusSignalдля анализа входов и задания типа выхода компонента. -
В ячейке Dimensions inheritance method — можно использовать данные
Fixedи значения из шин, чтобы определить размерности выходных сигналов. -
В ячейке Update method code — если блок имеет внутреннее состояние, поля типа
FixedилиBusSignalможно использовать для хранения или изменения этого состояния на каждом шаге симуляции. -
В ячейке Terminate method code — допускается использование этих типов для записи итогового состояния, если структура содержит соответствующие поля.
-
В ячейке Common types and dimensions inheritance method — если требуется одновременно обрабатывать и типы, и размерности,
FixedиBusSignalтакже могут участвовать в логике расчета.
Следовательно, Fixed, fi(…), fixdt(…), BusSignal и get_bus функции применимы во всех аспектах конфигурации и работы блока Engee Function — как на этапе выполнения, так и на этапе генерации типа и размерности сигнала. Их можно свободно использовать как в параметрах, так и в исходном коде во всех ячейках (при соблюдении правил их работы).
Аннотации
Аннотации в Engee Function позволяют отображать параметры блока прямо под его названием в модели. Для их добавления откройте окно настроек
блока Engee Function и перейдите на вкладку Аннотация. Выберите нужные маркеры свойств блока и добавьте их в текстовый редактор.

На этой вкладке:
-
Слева отображается список доступных параметров (кроме скрытых).
-
Справа находится текстовый редактор, где можно задать аннотацию с маркерами в формате
%<ИмяПараметра>. -
Перенос параметров возможен вручную, через автодополнение или с помощью кнопки
.
После выхода из редактора (например, при клике вне его) аннотация применяется: маркеры автоматически заменяются на фактические значения параметров и итоговый текст отображается под названием блока (или над ним, если название размещено сверху).
Для удаления аннотаций необходимо удалить соответствующий маркер в редакторе.
Доступные маркеры
Маркер свойств автоматически заменяется на актуальное значение параметра. Доступны следующие маркеры:
-
Порты:
%<Inputs>,%<Outputs>— количество входных и выходных портов;%<InputPort1Type>,%<OutputPort1Type>,%<InputPort1Size>,%<OutputPort1Size>— тип данных и размерность сигналов. -
Временные характеристики:
%<SampleTime>— дискретность;%<SampleTimeInheritanceMethod>— метод наследования дискретности. -
Ячейки кода:
%<ComponentStructCode>,%<StepMethodCode>— код структуры и метода шага. -
Параметры:
%<Parameters>,%<Parameter1Name>,%<Parameter1Value>— названия и значения параметров. -
Флаги включения:
%<DefineComponentStruct>,%<UseCommonCode>,%<DefineStepMethod>,%<DefineUpdateMethod>,%<DefineTerminateMethod>— включение соответствующих секций кода. -
Методы переопределения:
%<OverrideTypesInhMethod>,%<OverrideDimsInhMethod>,%<OverrideSampleTimeInhMethod>— настройки наследования типов. -
Прочее:
%<UseExternalCache>— использование внешнего кэша.
Функции преобразования и типизированные арифметические операции: econvert, esum, emul, ediv
В блоке Engee Function, а также в командной строке
Engee доступны функции, которые позволяют выполнять арифметические операции с указанием выходного типа, преобразовывать значения между типами с контролем округления и переполнения, а также заранее определять тип результата с помощью специализированных правил (_promote_type). Эти функции используются в логике Engee Function, написанной в ячейках исходного кода Step method code, Common code и других.
Доступные функции:
-
econvert— преобразование значений между типами; -
esum,esub,emul,ediv— арифметические операции с заданием выходного типа; -
emul!— матричное умножение с кэшированием результата; -
*_promote_type— функции выведения результата по входным типам.
| Эти функции могут применяться как к скалярам, так и к массивам. Результат всегда возвращается в типе, явно указанном пользователем (или выведенном автоматически), что делает поведение блока стабильным и ожидаемым. |
Поддерживаемые типы
Details
Все функции из этого набора работают со следующими типами:
-
Вещественные:
Float16,Float32,Float64; -
Целочисленные:
Int8,Int16,Int32,Int64,Int128,UInt8,UInt16,UInt32,UInt64,UInt128; -
Логические:
Bool; -
Фиксированные:
Fixed(создаются черезfi(…)илиfixdt(…)); -
Комплексные:
Complex{T}, гдеT— любой из указанных выше типов.
Функции можно использовать также с массивами и векторизованными выражениями с применением точечного синтаксиса (.). Далее рассмотрим функции подробнее.
Преобразование чисел с помощью econvert
Details
Функция econvert позволяет привести значение к заданному типу, задав метод округления и способ обработки переполнения:
econvert(type::Type{T1}, var::T2; rounding_mode::RoundingMode=RoundDown, overflow::Bool=true)
Здесь:
-
type— тип, в который нужно преобразовать значение. Еслиvar— комплексное число, указывается базовый вещественный тип. Например,econvert(Float32, Complex{Int16}(1 + 2im))вернетComplex{Float32}; -
var— значение для преобразования; -
rounding_mode— метод округления (RoundDown,RoundUp,RoundNearest,RoundNearestTiesAway,RoundToZero); -
overflow— еслиtrue, то используется поведение с переполнением (wrap); еслиfalse, включается насыщение (saturation) — значение ограничивается диапазоном типа.
Функция econvert может применяться для приведения типов перед сравнениями, внутри арифметических выражений, при обработке сигналов в условиях и других сценариях.
Арифметика с контролем типа с помощью esum, esub, emul, ediv, emul!
Details
Функции типизированных арифметических операций esum, esub, emul, ediv, emul! имеют одинаковый интерфейс:
esum(x1, x2, out_type, overflow=true, rm=RoundDown)
esub(x1, x2, out_type, overflow=true, rm=RoundDown)
emul(x1, x2, out_type, overflow=true, rm=RoundDown)
ediv(x1, x2, out_type, overflow=true, rm=RoundDown)
emul!(cache, x1, x2, out_type, overflow=true, rm=RoundDown)
Аргументы:
-
x1,x2— аргументы операции (скаляры или массивы); -
out_type— тип, в котором производится и возвращается результат; -
overflow— включение переполнения (true) или насыщения (false); -
rm— метод округления (см. список выше).
Функции esum, esub, emul, ediv возвращают значение, соответствующее указанному out_type.
Функция emul! используется для ускорения при работе с матрицами: она не создает новый массив, а записывает результат в уже выделенный cache. Важно, чтобы eltype(cache) совпадал с out_type.
Результат зависит от типа out_type: он влияет не только на финальный результат, но и на поведение промежуточных вычислений — что особенно важно для Fixed и Complex.
Выведение типа результата с помощью *_promote_type
Details
Если параметры блока (например, SumType или MulType) заданы как строка "Inherit", тип результата определяется автоматически на основе типов входных данных с помощью функций *_promote_type.
Если требуется вычислить тип результата какой-либо операции, то воспользуйтесь перечисленными функциями:
-
Определяет выходной тип при сложении
nзначений одного типаT:sum_promote_type(::Type{T}, n::Integer=1, hdlsetup::Bool=false) -
Определяет выходной тип при сложении двух разных типов
T1иT2поnзначений:sum_promote_type(::Type{T1}, ::Type{T2}, n::Integer=1, hdlsetup::Bool=false) -
Определяет выходной тип при сложении произвольного количества аргументов с разными типами:
sum_promote_type_from_inputs(types::Type...; hdlsetup::Bool=false) -
Определяет тип аккумулятора при сложении
nзначений одного типаT:sum_accumulator_promote_type(::Type{T}, n::Integer=1, hdlsetup::Bool=false) -
Определяет тип аккумулятора при сложении двух разных типов
T1иT2поnзначений:sum_accumulator_promote_type(::Type{T1}, ::Type{T2}, n::Integer=1, hdlsetup::Bool=false) -
Определяет тип аккумулятора при сложении произвольного количества аргументов с разными типами:
sum_accumulator_promote_type_from_inputs(types::Type...; hdlsetup::Bool=false) -
Определяет выходной тип при умножении значений одного типа
T:mul_promote_type(::Type{T}, hdlsetup::Bool=false) -
Определяет выходной тип при умножении двух различных типов
T1иT2:mul_promote_type(::Type{T1}, ::Type{T2}, hdlsetup::Bool=false) -
Определяет выходной тип при делении одного значения типа
T:div_promote_type(::Type{T}, hdlsetup::Bool=false) -
Определяет выходной тип при делении значения типа
T1на значение типаT2:div_promote_type(::Type{T1}, ::Type{T2}, hdlsetup::Bool=false)Здесь
hdlsetup— логический параметр, который определяет, нужно ли учитывать специфику аппаратной платформы (TargetHardware). По умолчанию:hdlsetup = TargetHardware == "C" ? false : true
Функции удобно использовать в ячейке Types inheritance method для точного определения выходного типа блока на основе входов и параметров.
Пример использования
Details
Ниже пример компонента, реализующего выражение a*x + b. Типы промежуточного умножения и финального сложения задаются через параметры MulType и SumType. Если указано "Inherit", то тип выводится автоматически.

Параметры Engee Function:
a = fi(5, 1, 16, 4)
b = fi(11, 1, 24, 7)
MulType = "Inherit"
SumType = "Inherit"
Код блока (в ячейке Common code, Dimensions inheritance method и Types inheritance method):
-
Ячейка Common code:
struct Block{SumType, MulType, aType, bType} <: AbstractCausalComponent a::aType b::bType function Block() sum_type = OUTPUT_SIGNAL_ATTRIBUTES[1].type mul_type = if MulType == "Inherit" mul_promote_type(INPUT_SIGNAL_ATTRIBUTES[1].type, eltype(a)) else MulType end new{sum_type, mul_type, typeof(a), typeof(b)}(a, b) end end -
Ячейка Dimensions inheritance method:
function propagate_dimensions(inputs_dimensions::Vector{<:Dimensions})::Vector{<:Dimensions} input_dimensions = first(inputs_dimensions) mock_input = zeros(input_dimensions) mock_output = mock_input .* a .* b return [size(mock_output)] end -
Ячейка Types inheritance method:
function propagate_types(inputs_types::Vector{DataType})::Vector{DataType} mul_type = if MulType == "Inherit" mul_promote_type(inputs_types[1], eltype(a)) else MulType end sum_type = if SumType == "Inherit" sum_promote_type(mul_type, eltype(b)) else SumType end return [sum_type] end
Таким образом, функции econvert, esum, emul, ediv и правила promote_type позволяют управлять типами и поведением вычислений внутри блока Engee Function. Они поддерживают все основные числовые типы, включая фиксированную точку и комплексные значения, и могут использоваться в вычислениях, сравнениях, логике наследования и настройке поведения компонентов с помощью блока Engee Function.
Диагностические функции warning, stop_simulation, pause_simulation, info
В исходном коде блока Engee Function также доступны функции warning, stop_simulation, pause_simulation и info, которые позволяют взаимодействовать с системой диагностики модели на этапе симуляции и выводить сообщения в окне диагностики
. Эти функции могут использоваться внутри следующих ячеек исходного кода:
-
Component struct code
-
Step method code
-
Update method code
-
Terminate method code
-
Common code
|
Функции Так, хотя эти функции формально допустимы в ячейке Component struct code, на практике их размещение там не имеет смысла: эта ячейка предназначена исключительно для описания структуры блока, а не для исполняемой логики. Чтобы диагностические функции работали корректно, их необходимо размещать внутри поддерживаемых методов симуляции: Step method code, Update method code, Terminate method code или в Common code, если там переопределяется соответствующий метод. Пример корректного размещения:
|
По аналогии можно переопределить любой поддерживаемый метод, вызываемый во время симуляции (например, update!, terminate, step), в ячейке Common code вручную, если отключить соответствующую стандартную ячейку (например, Define update method, Define step method и др.).
Для корректной работы в ячейке Common code функции warning, stop_simulation, pause_simulation и info могут использоваться:
-
Внутри функций, переопределяющих поведение методов, отвечающих за выполнение модели, таких как
step,update!,terminate!. Например, если функтор, обычно задаваемый в ячейке Step method code, определен в Common code, то диагностические функции внутри него будут работать корректно. -
Внутри вспомогательных функций, которые вызываются из методов, отвечающих за выполнение модели. То есть если вспомогательная функция определена в Common code и используется, например, в
step, то вызовы диагностических функций внутри нее также будут выполняться корректно.Переопределение update!или других методов не заменяет обязательную реализациюstep(function (c::Block)(t, in…)). Engee требует наличия этой функции как точки входа в симуляцию.
Пример корректного переопределения update! в Common code:
# Структура с Component struct code (если отключена, то должна быть обязательно вынесена в Common code)
struct Block <: AbstractCausalComponent
g::Real
function Block()
new(gain)
end
end
# Step method (обязательный метод, вызываемый на каждом шаге симуляции)
function (c::Block)(t::Real, in1)
c = update!(c, t, in1)
return c.g .* in1
end
# Переопределенный метод update!
function update!(c::Block, t::Real, in1)
if t == 5.0
info("update triggered at t = $t") # диагностическое сообщение
end
return c
end
В итоге будут получены следующие сообщения в окне диагностики модели:

Далее подробнее рассмотрим сами диагностические функции:
-
warning— функцияwarning(msg::String)выводит предупреждающее сообщение. Симуляция при этом продолжается. Это может быть полезно для указания на некритичные проблемы или состояния, которые требуют внимания, но не останавливают выполнение. Пример:if t == 5.0 || t == 7.5 warning("time == $t") end -
stop_simulation— функцияstop_simulation(msg::String)немедленно завершает симуляцию и выводит сообщение о завершении. Используется для указания на критическое условие, при котором продолжение моделирования невозможно или нежелательно. Пример:if t == 5.0 stop_simulation("time == $t") end -
pause_simulation— функцияpause_simulation(msg::String)приостанавливает выполнение симуляции и отображает указанное сообщение о паузе. Возобновление симуляции возможно вручную с помощью кнопки Продолжить:
Эта функция может быть полезна для анализа состояния модели в интересующий момент времени. Пример:
if t == 5.0 pause_simulation("time == $t") end -
info— функцияinfo(msg::String)выводит информационное сообщение. Используется для отображения промежуточных значений без влияния на выполнение симуляции. Пример:if t == 5.0 || t == 7.5 info("time == $t") end
Пример кода
В данном примере представлена упрощенная реализация блока Discrete-Time Integrator, основанная на интеграции кода Julia в модель Engee. В качестве метода интегрирования выбран прямой метод Эйлера. На вкладке Advanced блока Engee Function установите значение Discrete для параметра Sample time inheritance method. Далее заполните ячейки исходного кода следующим образом:
-
В ячейке Common code:
mutable struct Block{T} <: AbstractCausalComponent const dt::Float64 state::T gain::Float64 function Block() dt = OUTPUT_SIGNAL_ATTRIBUTES[1].sample_time.period state = initial_condition gain = k new{typeof(state)}(dt, state, gain) end end -
В ячейке Step method code:
return c.state -
В ячейке Update method code:
c.state += in1 * c.dt * c.gain return c
В итоге будет получен следующий исходный код:

Параметры initial_condition и k инициализируются на вкладке Parameters настроек блока Engee Function:

На первом шаге симуляции модели внутреннее состояние блока c.state инициализируется значением параметра initial_condition.
Затем на каждом шаге расчета блок возвращает внутреннее состояние c.state в качестве выходного сигнала и пересчитывает его значение в методе update!.
| Структура компонента переопределяется в ячейке Common code, а не в Component struct code, так как требуется более гибкое определение: структура должна быть изменяемой (mutable) и параметризованной типом T, соответствующим типу состояния. Стандартное определение в Component struct code подходит только для неизменяемых (immutable) и непараметризованных структур. |
Продвинутое использование
Блок Engee Function позволяет задать поведение компонента с помощью собственного кода без необходимости собирать его из готовых блоков. Это дает возможность вручную управлять типами и размерностями выходных сигналов, использовать кэширование для повышения производительности, отключать прямую передачу входов на выход и задавать собственный период обновления данных.
Так, на странице, размещенной в Сообществе Engee по ссылке представлены практические примеры продвинутого использования блока Engee Function:
-
Преобразование входных данных в вектор с переопределением выходных параметров;
-
Использование кэширования и строго типизированных структур для повышения производительности;
-
Реализация блоков без прямой передачи (Direct feedthrough) для разрыва алгебраических петель;
-
Задание пользовательского периода дискретизации выходного сигнала.