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

Специализированные подсистемы в Engee

В сообществе уже обсуждались различные методы разделение модели на смысловые и функциональные блоки с помощью подсистем и моделей ссылок. Однако, в Engee существуют специализрованные подсистемы, позволяющие создать и использовать конструкции программирования. Цель данной публикации - ознакомится с ними.

Action-Call подсистемы

Задача этих подсистем - срабатывать по условию, заданному как вызов функции. Здесь, вызов функции является событием, которое генерируется блоками If-else или Switch-Case. Применение таких подсистем позволяет промоделировать ветвление алгоритмов. Для этого предназначены блоки Оператор If и Оператор Switch Case. Пример использования этих блоков находится в модели function_callers.

Промоделируем такую конструкцию с помощью блока Оператор If:

if u > 0
	y = foo()
else
	y = bar()
end
image.png

Аналогично, с помощью блока Оператор Switch Case можно промоделировать конструкцию:

switch u
	case 1:
		y = foo_case()
	default:
		y = bar_case()
end
image.png

Вы наверняка заметили, что выходы подсистем подаются на блок Merge. Это специальный блок, который объединяет несколько входных сигналов в один. Главное правило - в конкретный момент времени "активным" должен быть только один сигнал. Под "активностью" понимается то, что у сигнала есть действительное значение.

На практике это работает так: когда осуществляется вызов подсистемы foo(), то на выходе этой подсистемы появляется значение, а в это время bar() неактивна и ее выход не имеет значения, и наоборот.

Так же следует обратить внимание на то, что на выходе блоков операторов if и switch case сигналы являются "вызовами" функций. Это специальный тип сигналов в Engee, предназначенный для "активации" подсистем, содержащих блок Порт действия (Action Port). В качестве примера использования этого блока рассмотрим подсистему foo:

image.png

Подсистемы с итераторами

Данные подсистемы предназначены для выполнения нескольких итераций своего содержимого за один шаг симуляции. Эти подсистемы являются прямыми аналогами циклов for, while и do…while. Примеры таких подсистем находятся в модели iterative_subs.

Важно!

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

For Iterator Subsystem

Данная подсистема реализует цикл for с помощью блока "Итератор For". Посмотрим на его настройки:

image.png

Для нас представляют интерес настройки итератора (переменной итерации). Например, можно задать свой итератор с помощью настройки Внешний источник переменной итерации. Далее, мы можем использовать значение внешнего сигнала, как источник количества итераций, а не вводить его вручную. Такая настройка позволяет гибко управлять количеством итераций:

image.png

Здесь, на вход VectorSig поступает некоторый векторный сигнал, затем он поступает на вход блока Probe. Блок Probe - это служебный блок, который извлекает информацию о сигнале, в том числе и количество элементов в сигнале. Затем, количество элементов передается в блок итератора как количество итераций:

image.png

Дополнительно, мы извлекаем значение нашего итератора с помощью настройки Вывод переменной итерации.

While Iterator Subsystem

Данная подсистема реализует условный цикл while или do…while с помощью блока Итератор While. Итерации будут выполняться до тех пор, пока на входе cond будет положительный ненулевой сигнал. Как и для For можно вывести значение итератора.

Как и в обычных языках программирования при применении таких циклов всегда есть опасность создания бесконечного цикла. Для защиты от бесконечного цикла у блока Итератор While есть настройка максимального количества итераций:

image.png

Рассмотрим различие между типами циклов while и do…while:

image.png image.png

В случае while у блока существует дополнительный вход IC - начальное логическое условие

Подсистемы For Each

Подсистемы For Each предназначены для работы с многомерными сигналами. Такая подсистема разделяет один или несколько входных сигналов на элементы и применяет свое содержимое на каждый элемент. Удобство заключается в том, расчет количества итераций происходит автоматически.

Такая подсистема - это прямой аналог цикла foreach, который есть во многих современных языках программирования.

Важно! В таких подсистемах как минимум один вход должен иметь настройку "Разделение":

image.png

Три следующие настройки позволяют настроить разделение входного многомерного сигнала.

В качестве примера воспользуемся моделью for_each_subsystem:

image.png

На вход этой подсистемы будет поступать вектор из двух синусоид, а внутри к каждому элементу вектора будет применена следующая операция:

image.png

Обратите внимание, что я дополнительно извлекаю номер итерации. Посмотрим как работает эта модель:

In [ ]:
fe_sub = engee.open(joinpath(@__DIR__,"for_each_subsystem.engee"))
res = engee.run(fe_sub)
Out[0]:
SimulationResult(
    run_id => 1,
    "Генератор синусоиды-1.1" => WorkspaceArray{Vector{Float64}}("for_each_subsystem/Генератор синусоиды-1.1")
,
    "For Each Subsystem.Выход1" => WorkspaceArray{Vector{Float64}}("for_each_subsystem/For Each Subsystem.Выход1")

)
In [ ]:
orig_sig = collect(res["Генератор синусоиды-1.1"])
proc_sig = collect(res["For Each Subsystem.Выход1"])
p1 = plot(orig_sig.time,stack(orig_sig.value,dims=1),label=["O1" "O2"])
p2 = plot(proc_sig.time,stack(proc_sig.value,dims=1),label=["O1" "O2"])
plot(p1,p2,layout = (2,1),link = :x)
Out[0]:

Первый график - это оригинальный сигнал, а нижний - результат работы подсистемы For Each

Выводы

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