Masks at Engee
Mask - is a customisable user interface for creating your own blocks. Blocks can be created based on any Engee library and subsystems blocks.
Mask simplifies usage and reuse of blocks. A masked block (with a mask applied) has its own settings window where you can change the parameters of the block. Masks allow passing parameters not only to a block or subsystem, but also to the source code of Engee Function and C Function blocks.
The mask allows:
-
Create your own parameters dialogue box to quickly configure the block/subsystem;
-
Change the appearance of the block/subsystem;
-
Hide the contents of the block/subsystem.
For example, there is a model of signal generator from blocks Switch, Sine Wave Function, Pulse Generator, Constant and Out1:
Using a mask from the subsystem, a custom block is created to switch the signal type via a control parameter. The mask passes the value to the Constant Value parameter of the Constant block, which changes the type of received signal in the Switch block. As a result, the model switches between Sine Wave and Pulse Generator signals depending on the selected mask value (`Rectangular Pulse' or `Sinusoidal'). For convenience, the icon of the subsystem displays the type of the selected signal.
The mask can be easily configured so that the values of its parameters automatically change the values of the block parameters when the values of its parameters change. However, the reverse effect is not possible: block parameter values cannot change mask parameter values. This restriction provides control over block settings through the mask and prevents accidental changes in the mask itself. |
The Mask Editor is a tool for customising a mask. To open the editor, right-click on the block and select Mask -> Add mask:
The editor opens in a new browser window:
After the mask is applied, the parameters of the mask are shown in the block settings. To see the parameters of the block itself, press Look Under Mask. To return, press Look Mask:
For subsystems, the option Look Under Mask takes you inside the subsystem rather than showing the parameters of the block. Use Model navigation bar to exit the subsystem. |
To edit or delete a mask, right-click on the icon of the block with already created mask and select Edit mask or Remove mask:
Mask Editor Interface
The mask editor contains two sets of tabs:
-
*Interface Editor
-
*Code Editor
Interface Editor
Interface Editor - section of adding controls mask, structural elements and tabs to the settings window.
-
Structure - containers for placing and hiding controls:
-
1.1. Hidden section
- a container that can be hidden or revealed by clicking, allowing you to control the display of multiple elements at the same time.
Hidden section without added controls will not be shown in the mask. The section by default can be renamed.
-
-
Controls elements - main interface components from which the block mask is formed. Each control has three parameters:
-
Parameter label - must match the name of the variable in the mask to be set;
-
Label - will be displayed in the settings window;
-
Value or list item - defines the value by default.
In addition to parameters, you can configure attributes:
-
Hidden - hides the control;
-
Evaluate - analyses the entered value, interprets it and stores the appropriate data type (e.g. number, string or array). If an inappropriate data type is entered, the system will generate an error.
-
-
2.1. Text input
- adds a mask parameter with an input text or numeric value. Data type: Any;
-
2.2. Checkbox
- area for ticking the checkbox. Data type: Bool;
-
2.3. Select
- displays a list of available options from which one is selected. To add items to the drop-down list, enter them one by one in the List items field and press Enter after each value. Data type: String;
A drop-down list without parameters gives an error and will not save the mask.
-
Mask space - area for creating a mask using controls and structural elements. All elements added to the area will be exactly transferred to the mask after saving. To add elements, drag and drop them with the mouse:
To delete an item, highlight it and press Delete.
To add a new tab, press + in the right corner of the mask space:
-
Save button - saves the mask. Alternatively, the hotkeys Ctrl+S (Win/Linux) and ⌘+S (macOS) can be used.
-
Callback editor button - opens the section Code editor.
-
Hide left panel/show left panel.
-
Hide right pane/show right pane.
Code Editor
Code editor - section for customising controls using callback functions (callbacks). Each control is configured with its own unique callbacks.
-
Names of mask parameters (can be changed in the interface editor).
-
Field name - indicates the label or field type of the parameter (can be changed in the interface editor). It gives a more detailed description of the purpose or function of the parameters.
-
Callbacks code space - area for setting callbacks for a particular mask parameter.
-
Save button - saves the mask. Alternatively, the hotkeys Ctrl+S (Win/Linux) and ⌘+S (macOS) can be used.
-
Hide left pane/show left pane.
-
Hide right pane/show right pane.
Callbacks
Callbacks are functions that are called automatically in response to certain actions. Callbacks in Engee are written in the language Julia, are not executed during simulation and are used to control both individual parameters and the mask as a whole. Local callbacks are used to control individual parameters, while global callbacks are used for the mask as a whole:
-
Global callbacks (Global) - are associated with the entire mask and are executed independently of specific parameters. Global callbacks are iconDrawCallback и blockChangedCallback. Global calls have access to all local calls and can control them. For example, you can pass data from a local callback to a global callback blockChangedCallback. This will allow you to use the global call to customise the actions of the local call.
-
Local callbacks - are bound to specific parameters of the mask and are triggered only when those parameters are changed. All callbacks except iconDrawCallback и blockChangedCallback, are local. Local callbacks can only work with parameters to which they are bound in the interface editor, and cannot directly change parameters of other callbacks (Checkbox can’t control the drop-down list, global callbacks are used for that).
To work with a mask parameter, it must first be obtained. To do this, a special parameter object is used, which is stored in the mask
variable. For example, to get the parameters Text input with the name
text_input_1
, you can use the following code:
mask.parameters.text_input_1
The parameters object has three main properties:
-
name::String
- field name (read-only); -
value::Any
- value of the parameter (readable and writable); -
hidden::Bool
- visibility of the parameters (available for reading and writing).
To change a parameter, always assign a new value to the
|
The gcb()
function is used to get the path to the masked block. The path is returned as a string. This allows you to control the block and its internal components with code. Let’s say you want to pass a value from the parameters Dropdown list (
dropdown_1
) to the internal block LDL Factorization:
LDLPath = gcb() * "/LDL Factorization"
engee.set_param!(LDLPath, "NonPositive" => mask.parameters.dropdown_1.value)
Here gcb()
gets the path to the current block. The path is used to find the LDL Factorisation block, after the value from dropdown_1
is passed to the NonPositive
block parameters. In this way, the gcb()
function helps to link the mask parameters to other parts of the model.
The engee.gcb() and engee.gcm() methods cannot be used in callbacks inside programme control.
|
Overview of callbacks
The order in which the mask callbacks are run:
-
For each parameter that is changed, the following is called validateCallback. Occurs first to ensure that parameters have valid values before any further operations are performed;
-
For each changed parameter, calls . valueChangedCallback. After each callback is called validateCallback for each newly changed parameter, as changing the value may affect other parameters;
-
Runs blockChangedCallback. For each newly changed parameters is called for each newly changed parameter validateCallback.
-
Runs iconDrawCallback.
Knowing the order in which the callbacks are run is important for the block mask to work properly, as changing one parameter can affect the others. Incorrect order can lead to errors in value checking or incorrect icon updates. Observing the correct order ensures that all dependencies are taken into account and the block works correctly.
iconDrawCallback - shaping the appearance of the unit
Details
To work with iconDrawCallback function engee.show()
is necessarily used. The icon with the wrong iconDrawCallback looks like this:
iconDrawCallback can display text, number, graph, picture or formula (LaTeX). For example:
-
Number output (similar for text):
engee.show(text_input_1)
->
-
SVG output:
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> """ )
It is recommended to use SVG icons for blocks, as they take up less space and automatically adjust to the block size without loss of quality. -
LaTeX formula output. To do this, use the capital letter and the inverted commas
""
. The formula is written in quotes according to the classical LaTeX syntax:engee.show(L"\lvert u \rvert")
-
Graph output:
x = range(0, 2*pi, 1000); y = sin.(x); engee.show(plot(x, y))
-
Picture output:
using Base64 img = "...base64-text..." engee.show(Images.load(IOBuffer(base64decode(img))))
Getting a base64
representation of an image can be done in different ways:
-
Via Julia script:
using Base64 image_data = read("path_to_file") base64_encoded = base64encode(image_data) println(base64_encoded)
-
Via command line
(prefetch into shell mode by clicking ;):
base64 --wrap 0 "path_to_file"
-
Via external services, e.g. base64decode.org.
You can change the block port signature using the engee.port_label()
function via the callback mechanism. The example below shows how to display text on the block icon and set signatures for different ports:
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' для выходного порта
Signatures can also be set for non-directional ports. You can specify an empty name:
engee.port_label("acausal", 1, "") # Пустое имя для ненаправленного порта
SVG files and LaTeX formulas can be displayed in the port signature:
engee.port_label("input", 1, svg="...")
engee.port_label("input", 1, L="...")
The port numbering is the same as the software control. If an incorrect port number or type is specified, the function will be ignored.
blockChangedCallback - is executed after changing any parameters of the mask
Details
Runs when any mask parameter is changed, but after all other callbacks have been executed. Being a global callback, it has access to both variables and the mask object. Examples:
-
Changing a parameter in a subsystem:
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
here the value of the mask parameter is synchronised with the parameter of the LDL Factorization block.
mode
is a variable that stores the current value of thedropdown_1
parameters. For example, if you change the value of the parameters fromWarning
toError
in the dropdown list, the same parameter will be similarly changed in the LDL Factorisation block in the subsystem.
valueChangedCallback - is performed when changing the value of parameters
Details
valueChangedCallback is needed to hide parameters or to change the states of the subsystem using the gcb()
function. The callback is launched when the value of the linked parameters is changed. Parameters are linked if the name of the parameter of the corresponding control coincides with the callback code, for example:
->
Examples:
-
Hiding parameters:
mask.parameters.text_input_1.hidden = checkbox_1
here the parameters are hidden (hidden) when pressed Checkbox and visible when Checkbox pressed.
-
Changing the subsystem status (usage of programme control functions):
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
here is set the connection with Checkbox (
checkbox_1
), which when switched on (the checkbox is active) adds a Model block to the subsystem and deletes it when switched off (the checkbox is unchecked).
validateCallback - checks the correctness of the value (validation) of the parameters
Details
The callback checks the correctness of the value validateCallback and, in case of an incorrect value, shows an error. All error messages except AssertionError will be considered an error of the callback itself, not an error of the entered data:
The correctness of the parameters value is checked using the value
variable. validateCallback always starts with the macro @assert.
validateCallback is available only for the control Text input and is bound to its callback valueChangedCallback.
Examples:
-
Checking the correctness of a value:
@assert value > 0 "Значение должно быть больше нуля"
-
Checking the entered type:
@assert value isa Number "Значение должно быть числом"
Examples
Example of passing the mask parameters to the source code of a C Function block
-
Place a C Function block in the Engee workspace. Right-click on the block, select Mask -> Add mask.
-
In the Mask Interface Editor add Input Field
.
-
Go to the mask code editor and in the left parameters menu select the parameter Text input (by default it is
text_input_1
) and move it to the mask space. Do this twice so that each parameter of the C Function block has its own input field: -
Use the following code in the callback 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)
This code sets the path to the current C Function block using the
gcb()
function, then reads the values fromtext_input_1
andtext_input_2
that correspond to the param1 and param2 block parameters. A line of C code is then created that defines theadd_numbers
function that adds two integers and uses the inputted values to compute the result. Theengee.set_param!
is used to update the "OutputCode" parameters of the C Function block, setting the generated code. -
Now, when editing the mask parameters, the parameters of the C Function block are also changed, and their changed values are included in the source code in the OutputCode tab:
A similar approach is implemented for the other source code tabs of the C Function block - StartCode and TerminalCode , as well as for the tabs of the Engee Function block - ExeCode and InhMethodsCode .
|
It is possible to use not only numeric values, for example:
engee.set_param!(gcb(), "OutputCode"=>"print($text_input_1)")
To do this, in the mask interface editor, when creating or editing a parameter, you must specify a value whose data type will subsequently be changed. If the data type does not match, the system will display an error:
It is important to ensure that the "Calculate" checkbox is selected, as this will allow the mask to retain the parameters data type after saving:
Example of passing the mask parameters to the source code of the Engee Function block
-
Assemble the model from the blocks Sine Wave Function, Engee Function and Terminator and include record
signal as shown in the figure:
-
In the source code of the Engee Function block, add the following code:
struct Block <: AbstractCausalComponent end function (c::Block)(t::Real, x) return gain .* x end
-
Open the mask editor for the Engee Function block, click on the block, select Mask -> Add mask. In the mask editor add Text input
, name the parameters Gain and set its value, for example 3:
-
In the parameters of the Engee Function block, set the gain parameter as shown in the figure:
This approach allows you to use the mask parameter value from the block settings by adding it to the source code and applying the parameter name to get the specified value:
-
Let’s test this approach with a graph by running a simulation of the model:
-
The value of the gain parameter is indeed 3, which means that the mask works fine with the source code of the Engee Function block.
_ Example of a customisable subsystem (block Subsystem)_
Masks can be overlaid on top of blocks Subsystem. Let’s consider a case in which we need to control parameters of subsystem blocks. For example, the parameters of the block Sine Wave Function:
-
By default, the subsystem has no parameters other than Treat as atomic unit:
Go to the subsystem and add a Sine Wave unit to it.
-
Right-click the icon of the subsystem, select Mask -> Add mask.
-
In the mask interface editor add Dropdown list
. Add Sample based and Time based parameters to the list:
-
In the mask code editor, go to the Global tab and in the callback iconDrawCallback add the following code:
engee.show(dropdown_1)
This code will display the current value of dropdown_1 parameter (dropdown list parameter) on the subsystem icon.
-
In the callback blockChangedCallback use the following code:
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
This code changes the
"SineType"
parameter of the Sine Wave block in the subsystem depending on the value of the dropdown_1 mask parameter: if"Time based"
is selected,"SineType"
=>"Time based"
is set, if"Sample based"
is selected,"SineType"
=>"Sample based"
is set.
By changing the value of the Sine type parameter in the masked subsystem, this parameter is automatically changed in the Sine Wave block as well.
Time based is selected |
Sample based is selected |
Example of passing the mask parameters to the Chart block
Mask parameters can be passed to the block Chart. For example, create a model from the blocks Constant, Subsystem and To CSV as shown in the figure:
Add the Chart block to the subsystem. To connect it to the input (In1) and output (Out1) of the subsystem, open the Chart block and create input (state-machines/chart.adoc#inport-state-machines) and output (state-machines/chart.adoc#outport-state-machines) ports via settings window . Also add two local variables:
-
local_input with the value
input
; -
local_c with the value
c
.
These variables will receive values from the subsystem mask.
After creating state , give it a name and write an expression using the local variables, inputs and outputs. Make sure that the names of variables in the state coincide with those specified in the settings of the Chart block. The result will be a finite automaton model with inputs, outputs and variables configured:
Now the model within the subsystem will look like this:
Using masks, create the following Text input for the Chart block:
Go to the top level of the model using navigation bar and for the Subsystem block, create a mask with the following Text input :
Run the model . When the simulation is complete, a CSV file will be created in file browser
that shows the simulation results by time:
time,1
0.0,22.0
0.01,22.0
0.02,22.0
0.03,22.0
0.04,22.0
0.05,22.0
0.06,22.0
0.07,22.0
0.08,22.0
...
9.96,22.0
9.97,22.0
9.98,22.0
9.99,22.0
10.0,22.0
The result is 22
, which confirms the correctness of the expression calculation in the Chart block state: local_c = c = 6
(from the Subsystem mask), local_input = input = 15
(from the Chart block mask), their sum is equal to 21
, and the Constant block adds another 1
, getting the final value 22
.