Маски в Engee
Маска — это настраиваемый пользовательский интерфейс для создания собственных блоков. Блоки могут быть созданы на основе любых блоков библиотеки Engee и подсистем.
Маска упрощает использование и переиспользования блоков. У маскированного блока есть собственное окно настроек, в котором можно менять параметры блока. Маски позволяют передать параметры не только в блок или подсистему, но и в исходный код блоков Engee Function и C Function.
Маска позволяет:
-
Создавать собственное диалоговое окно параметров для быстрой настройки блока/подсистемы;
-
Изменять внешний вид блока/подсистемы;
-
Скрывать содержимое блока/подсистемы.
Например, есть модель генератора сигналов из блоков Switch, Sine Wave, Pulse Generator, Constant и Outport:
С помощью маски из подсистемы создан пользовательский блок, позволяющий переключать тип сигнала через управляющий параметр. Маска передает значение в параметр Constant Value блока Constant, что изменяет тип принимаемого сигнала в блоке Switch. В результате модель переключается между сигналами Sine Wave и Pulse Generator в зависимости от выбранного значения маски (Прямоугольный импульсный сигнал
или Синусоидальный
). Для удобства на иконке подсистемы отображается тип выбранного сигнала.
Маска легко настраивается так, чтобы при изменении значений ее параметров автоматически менялись значения параметров блока. Однако, обратное влияние невозможно: значения параметров блока не могут менять значения параметров маски. Это ограничение обеспечивает контроль над настройками блока через маску и предотвращает случайные изменения в самой маске. |
Редактор масок — это инструмент для настройки маски. Для открытия редактора нажмите на блок правой кнопкой мыши и выберите Маска → Добавить маску:
Редактор открывается в новом окне браузера:
После применения маски в настройках блока показываются параметры маски. Чтобы увидеть параметры самого блока, нажмите Посмотреть под маску. Чтобы вернуться, нажмите Посмотреть маску:
Для подсистем опция «Посмотреть под маску» переносит внутрь подсистемы, а не показывает параметры блока. Используйте панель навигации для выхода из подсистемы. |
Для редактирования или удаления маски нажмите правой кнопкой мыши по иконке блока с уже созданной маской и выберите Редактировать маску или Удалить маску:
Интерфейс редактора масок
Редактор масок содержит два набора вкладок:
-
Редактор интерфейса
-
Редактор кода
Редактор интерфейса
Редактор интерфейса — раздел добавления в окно настроек маски элементов управления, структурных элементов и вкладок.
-
Структурные элементы — контейнеры для размещения и скрытия элементов управления:
-
1.1. Скрываемая секция — контейнер, который можно скрыть или раскрыть по нажатию, позволяя управлять отображением нескольких элементов одновременно.
Скрываемая секция без добавленных элементов управления не будет показана в маске. Секцию по умолчанию можно переименовать.
-
-
Элементы управления — основные компоненты интерфейса из которых формируется маска блока. Каждый элемент управления имеет три параметра:
-
Название параметра — должно совпадать с именем переменной в маске, которую нужно настроить;
-
Название поля — будет отображаться в окне настроек;
-
Значение или элемент списка — определяет значение по умолчанию.
Помимо параметров можно настроить атрибуты:
-
Скрыт — скрывает элемент управления;
-
Вычислять — анализирует введенное значение, интерпретирует его и сохраняет соответствующий тип данных (например, число, строку или массив). При вводе несоответствующего типа данных система выдаст ошибку.
-
-
2.1. Поле для ввода — добавляет параметр маски с вводимым текстовым или числовым значением. Тип данных: Any;
-
2.2. Чекбокс — область для проставления флажка. Тип данных: Bool;
-
2.3. Выпадающий список — отображает список доступных опций, из которых выбирается одна. Чтобы добавить элементы в выпадающий список, вводите их по одному в поле Элементы списка и нажимайте Enter после каждого значения. Тип данных: String;
Выпадающий список без параметров выдает ошибку и не позволяет сохранить маску.
-
Пространство маски — область для создания маски с помощью элементов управления и структурных элементов. Все добавленные в область элементы будут в точности перенесены в маску после сохранения. Для добавления элементов перетащите их с помощью мыши:
Для удаления элемента выделите его и нажмите клавишу Delete.
Для добавления новой вкладки нажмите + в правом углу пространства маски:
-
Кнопка сохранения — сохраняет маску. Альтернативно можно использовать горячие клавиши Ctrl+S (Win/Linux) и ⌘+S (macOS).
-
Кнопка редактора обратных вызовов — открывает раздел Редактор кода.
-
Скрыть левую панель/показать левую панель.
-
Скрыть правую панель/показать правую панель.
Редактор кода
Редактор кода — раздел настроек элементов управления с помощью функций обратного вызова (callbacks). Для каждого элемента управления задаются собственные уникальные обратные вызовы.
-
Названия параметров маски (можно изменить в редакторе интерфейса).
-
Название поля — указывается метка или тип поля параметра (можно изменить в редакторе интерфейса). Оно дает более подробное описание назначения или функции параметра.
-
Пространство кода обратных вызовов — область для настройки обратных вызовов у конкретного параметра маски.
-
Кнопка сохранения — сохраняет маску. Альтернативно можно использовать горячие клавиши Ctrl+S (Win/Linux) и ⌘+S (macOS).
-
Скрыть левую панель/показать левую панель.
-
Скрыть правую панель/показать правую панель.
Обратные вызовы
Обратные вызовы — функции, которые вызываются автоматически в ответ на определенные действия. Обратные вызовы в Engee пишутся на языке Julia, не исполняются во время симуляции и используются для управления как отдельными параметрами, так и всей маской в целом. Для управления отдельными параметрами используются локальные обратные вызовы, а для всей маски в целом — глобальные:
-
Глобальные обратные вызовы (Global) — связаны со всей маской и выполняются независимо от конкретных параметров. Глобальными обратными вызовами являются
iconDrawCallback
иblockChangedCallback
. Глобальные вызовы имеют доступ ко всем локальным вызовам и могут управлять ими. Например, можно передать данные из локального вызова в глобальныйblockChangedCallback
. Это позволит использовать глобальный вызов для настройки действий локального. -
Локальные обратные вызовы — привязаны к конкретным параметрам маски и срабатывают только при изменении этих параметров. Все обратные вызовы, кроме
iconDrawCallback
иblockChangedCallback
, являются локальными. Локальные обратные вызовы могут работать только с параметрами, к которым они привязаны в редакторе интерфейса, и не могут напрямую изменять параметры других обратных вызовов (Чекбокс не может управлять выпадающим списком, для этого используются глобальные обратные вызовы).
Чтобы работать с параметром маски, его сначала нужно получить. Для этого используется специальный объект параметра, который хранится в переменной mask
. Например, чтобы получить параметр текстового поля с именем text_input_1
, можно использовать следующий код:
mask.parameters.text_input_1
У объекта параметра есть три основных свойства:
-
name::String
— имя поля (доступно только для чтения); -
value::Any
— значение параметра (доступно для чтения и записи); -
hidden::Bool
— видимость параметра (доступно для чтения и записи).
Чтобы изменить параметр, всегда присваивайте новое значение атрибуту
|
Для получения пути до блока с маской используется функция gcb()
. Путь возвращается в виде строки. Это позволяет управлять блоком и его внутренними компонентами с помощью кода. Допустим, нужно передать значение из параметра выпадающего списка dropdown_1
во внутренний блок LDL Factorization:
LDLPath = gcb() * "/LDL Factorization"
engee.set_param!(LDLPath, "NonPositive" => mask.parameters.dropdown_1.value)
Здесь gcb()
получает путь к текущему блоку. Путь используется, чтобы найти блок LDL Factorization, после значение из dropdown_1
передается в параметр NonPositive
блока. Таким образом, функция gcb()
помогает связать параметры маски с другими частями модели.
В обратных вызовах внутри программного управления нельзя использовать методы engee.gcb() и engee.gcm() .
|
Обзор обратных вызовов
Порядок запуска обратных вызовов маски:
-
Для каждого измененного параметра вызывается
validateCallback
. Происходит первым, чтобы гарантировать, что параметры имеют допустимые значения до выполнения любых дальнейших операций; -
Для каждого измененного параметра вызывается
valueChangedCallback
. После каждого обратного вызова вызываетсяvalidateCallback
для каждого вновь измененного параметра, так как изменение значения может повлиять на другие параметры; -
Запускается
blockChangedCallback
. Для каждого вновь измененного параметра вызываетсяvalidateCallback
. -
Запускается
iconDrawCallback
.
Знание порядка запуска обратных вызовов важно для правильной работы маски блока, так как изменение одного параметра может повлиять на другие. Неправильный порядок может привести к ошибкам в проверке значений или неправильному обновлению иконки. Соблюдение правильного порядка гарантирует, что все зависимости учтены и блок работает корректно.
iconDrawCallback — формирование внешнего вида блока
Details
Для работы с iconDrawCallback
обязательно используется функция engee.show()
. Иконка с неправильным iconDrawCallback
выглядит так:
iconDrawCallback
может отображать текст, число, график, картинку или формулу (LaTeX). Например:
-
Вывод числа (аналогично для текста):
engee.show(text_input_1)
→
-
Вывод SVG:
engee.show( svg""" <svg xmlns:ns0="http://www.w3.org/2000/svg" width="78%" height="80%" viewBox="0 0 26 27" fill="none"> <path vector-effect="non-scaling-stroke" d="M13 0.5L13 25.7282" stroke="#DDDDDD" stroke-linecap="round" /> <path vector-effect="non-scaling-stroke" d="M0.5 13H25.5" stroke="#DDDDDD" stroke-linecap="round" /> <path vector-effect="non-scaling-stroke" d="M24.1894 8.46273L13 8.46273L13 17.4628L2.18942 17.4627" stroke="#212121" stroke-linecap="round" /> </svg> """ )
Рекомендуется использовать SVG-иконки для блоков, так как они занимают меньше места и автоматически подстраиваются под размер блока без потерь качества. -
Вывод LaTeX-формулы. Для этого используйте заглавную букву и кавычки "". Формула записывается в кавычки по классическому синтаксису LaTeX:
engee.show(L"\lvert u \rvert")
-
Вывод графика:
x = range(0, 2*pi, 1000); y = sin.(x); engee.show(plot(x, y))
-
Вывод картинки:
using Base64 img = "...base64-text..." engee.show(Images.load(IOBuffer(base64decode(img))))
Получить base64
представления изображения можно разными способами:
-
Через Julia-скрипт:
using Base64 image_data = read("path_to_file") base64_encoded = base64encode(image_data) println(base64_encoded)
-
Через командную строку (предварительное перейдите в режим оболочки shell, нажав ;):
base64 --wrap 0 "path_to_file"
-
Через внешние сервисы, например base64decode.org.
Можно изменить подпись порта блока с помощью функции engee.port_label()
через механизм обратного вызова. Пример ниже показывает, как выводить текст на иконку блока и задавать подписи для разных портов:
engee.show("Some text") # Вывод текста на иконку блока
engee.port_label("input", 1, "foo_1") # Подпись 'foo_1' для первого входного порта
engee.port_label("input", 2, "foo_2") # Подпись 'foo_2' для второго входного порта
engee.port_label("output", 1, "bar") # Подпись 'bar' для выходного порта
Подписи можно задавать и для ненаправленных портов. Можно задавать пустое имя:
engee.port_label("acausal", 1, "") # Пустое имя для ненаправленного порта
В подписи портов можно выводить SVG-файлы и формулы LaTeX:
engee.port_label("input", 1, svg="...")
engee.port_label("input", 1, L="...")
Нумерация портов совпадает с программным управлением. Если указать неверный номер или тип порта, функция будет проигнорирована.
blockChangedCallback — выполняется после изменения любого параметра маски
Details
Запускается при изменении любого параметра маски, но после исполнения всех остальных обратных вызовов. Будучи глобальным обратным вызовом, имеет доступ как к переменным, так и к объекту маски.
Примеры:
-
Изменение параметра в подсистеме:
LDLPath = gcb() * "/LDL Factorization" mode = dropdown_1 if mode == "Ignore" engee.set_param!(LDLPath, "NonPositive" => "Ignore") elseif mode == "Warning" engee.set_param!(LDLPath, "NonPositive" => "Warning") elseif mode == "Error" engee.set_param!(LDLPath, "NonPositive" => "Error") end
здесь значение параметра маски синхронизировано с параметром блока LDL Factorization.
mode
— переменная, которая хранит текущее значение параметраdropdown_1
. Например, если в выпадающем списке поменять значение параметра сWarning
наError
, то этот же параметр аналогично поменяется у блока LDL Factorization в подсистеме.
valueChangedCallback — выполняется при изменении значения параметра
Details
valueChangedCallback
нужен для скрытия параметров или для изменения состояний подсистемы с помощью функции gcb()
. Обратный вызов запускается при изменении значения связанного параметра. Параметры связаны в том случае, если название параметра соответствующего элемента управления совпадает с кодом обратного вызова, например:
→
Примеры:
-
Скрытие параметров:
mask.parameters.text_input_1.hidden = checkbox_1
здесь параметр скрыт (hidden) когда нажат чекбокс и виден, когда чекбос отжат.
-
Изменение состояния подсистемы (использование функций программного управления):
if checkbox mask.parameters.checkbox.hidden = false engee.add_block("/Basic/Ports & Subsystems/Model", gcb() * "/Model") else mask.parameters.checkbox.hidden = true engee.delete_block(gcb() * "/Model") end
здесь задана связь с чекбоксом
checkbox_1
, при включении (флажок активен) которого в подсистему добавляется блок Model и удаляется при выключении (флажок убран).
validateCallback — проверяет корректность значения (валидацию) параметра
Details
Обратный вызов проверяет корректность значения valueChangedCallback
и, в случае некорректного значения, показывает ошибку. Все сообщения об ошибках кроме AssertionError будут считаться ошибкой самого обратного вызова, а не ошибкой введенных данных:
Корректность значения параметра проверяется с помощью переменной value
. validateCallback
всегда начинается с макроса @assert.
validateCallback
доступен только для элемента управления Поле для ввода и привязан к его обратному вызову valueChangedCallback
.
Примеры:
-
Проверка корректности значения:
@assert value > 0 "Значение должно быть больше нуля"
-
Проверка введенного типа:
@assert value isa Number "Значение должно быть числом"
Примеры
Пример передачи параметра маски в исходный код C Function
-
Поставьте блок C Function в рабочее пространство Engee. Нажмите по блоку правой кнопкой мыши, выберите Маска/Добавить маску.
-
В редакторе интерфейса масок добавьте Поле для ввода .
-
Перейдите в редактор кода масок и в левом меню параметров выберите параметр поля для ввода (по умолчанию это
text_input_1
) и перенесите его на пространство маски. Сделайте так два раза, чтобы для каждого параметра блока C Function было свое поле для ввода: -
В обратном вызове blockChangedCallback используйте следующий код:
# Установка пути к текущему блоку CFunctionPath = gcb() # Получение значений параметров масок param1 = text_input_1 param2 = text_input_2 # Формирование кода на Cи в зависимости от значений параметров c_code = """ int add_numbers(int param1, int param2) { return param1 + param2; } int result = add_numbers($param1, $param2); """ # Установка параметра "OutputCode" в блоке C Function engee.set_param!(CFunctionPath, "OutputCode" => c_code)
В данном коде устанавливается путь к текущему блоку C Function с помощью функции
gcb()
, после чего считываются значения изtext_input_1
иtext_input_2
, которые соответствуют параметрам блока param1 и param2. Затем создается строка кода на языке Cи, которая определяет функциюadd_numbers
, складывающую два целых числа, и использует введенные значения для вычисления результата. С помощьюengee.set_param!
обновляется параметр "OutputCode" блока C Function, устанавливая сформированный код. -
Теперь при редактировании параметров маски меняются и параметры блока C Function, а их измененные значения попадают в исходный код во вкладку OutputCode:
Аналогичный подход реализуется и для других вкладок исходного кода блока C Function - StartCode и TerminalCode , а также для вкладок блока Engee Function - ExeCode и InhMethodsCode .
|
Можно использовать не только числовые значения, например:
engee.set_param!(gcb(), "OutputCode"=>"print($text_input_1)")
Для этого в редакторе интерфейса маски при создании или редактировании параметра необходимо указать значение, тип данных которого впоследствии будет изменяться. Если тип данных не совпадет, то система отобразит ошибку:
Важно убедиться, что установлен флажок «Вычислять», так как это позволит маске сохранить тип данных параметра после сохранения:
Пример настраиваемой подсистемы
Маски могут быть наложены поверх блоков Subsystem. Рассмотрим случай, в котором требуется управлять параметрами блоков подсистемы. Например, параметрами блока Sine Wave:
-
По умолчанию подсистема не имеет никаких параметров кроме Treat as atomic unit:
Зайдите в подсистему и добавьте в нее блок Sine Wave.
-
Нажмите правой кнопкой мыши по иконке подсистемы, выберите Маска → Добавить маску.
-
В редакторе интерфейса масок добавьте Выпадающий список . В список добавьте параметры Sample based и Time based:
-
В редакторе кода масок перейдите во вкладку Global, в обратном вызове iconDrawCallback и добавьте следующий код:
engee.show(dropdown_1)
Этот код будет отображать на иконке подсистемы текущее значение параметра dropdown_1 (параметр выпадающего списка).
-
В обратном вызове blockChangedCallback используйте следующий код:
SinePath = gcb() * "/Sine Wave" mode = dropdown_1 if mode == "Time based" engee.set_param!(SinePath, "SineType" => "Time based") elseif mode == "Sample based" engee.set_param!(SinePath, "SineType" => "Sample based") end
Этот код изменяет параметр
"SineType"
блока Sine Wave в подсистеме в зависимости от значения параметра маски dropdown_1: если выбрано"Time based"
, устанавливается"SineType"
⇒"Time based"
, если"Sample based"
, то"SineType"
⇒"Sample based"
.
Изменяя значение параметра Sine type в маскированной подсистеме, этот параметр автоматически изменяется и в блоке Sine Wave.
Выбран Time based |
Выбран Sample based |
|
|