C Function
Using C code in models.
blockType: CFunction
Path in the library:
|
Description
Block C Function allows the use of C code and shared libraries with a C API in Engee models.
When connecting a dynamic library that uses internal static variables, you should make sure that it provides an API for initializing/deinitializing these variables. The corresponding functions must be called in the tabs. StartCode and TerminateCode the source code editor. Otherwise, static variables will keep their latest values between simulations, which can lead to incorrect results when the model is run again.
|
Using
To integrate the C source code into the Engee model, you must:
-
Add a block to the model C Function from the Basic section/Custom functions Block libraries;
-
Click on the Edit Source Code button, after which the source code editor will be opened.

Source Code Editor
The workspace of the source code editor consists of four tabs:
-
OutputCode— contains the code that is executed at each step of the model calculation; -
StartCode— contains the code executed once during initialization of the model; -
TerminateCode— contains a code that runs once when the model is stopped; -
SharedCode— contains code that allows the exchange of global (shared) variables and functions between instances of the block C Function.
The syntax of the C language is highlighted in each section.
In the section OutputCode There is a block of special comments relevant to the simulator and the code generator.
Comments allow you to associate variables in the source code with the signals (inputs and outputs) of the block. C Function, as well as specify additional information needed to build the code and use it in the model.

Function Interface Hint
To access the inputs, outputs, parameters, and operating variables of the block correctly. C Function it is important to know the signature (interface) of these functions. Previously, you had to figure out its composition yourself, which made writing code more difficult and could lead to errors.
Now on all tabs of the block source code C Function, except for the tab SharedCode The Information area is displayed in the lower-left corner of the editor, where the interface (prototype) of the function is automatically generated and displayed, corresponding to the current settings of the block (inputs, outputs, parameters and working variables set on the tabs Basic, Parameters and Work Variables):

This allows you to see not only the function body, but also its exact interface when writing code, for example:
void CFunction_step(double t, uint32_t in1, double param1, uint32_t *out1);
The hint is updated dynamically when the block configuration is changed and helps to avoid errors when accessing inputs, outputs, parameters and working variables inside the source code.
Features of generating the C code from the block C Function
Block C Function supports C code generation. When generating the code, the block C Function it turns into functions that wrap the contents of all four sections of the source code.
At the moment, the C code generator does not provide additional information for the build system, which would be based on the block directives. C Function. The directives are specified in the section Build options block settings:

Reuse of the block
Tab SharedCode the source code of the block C Function allows you to set a common code available for exchanging data and functions between block instances. C Function with the same name in the parameter Function name.
In order to use the functionality SharedCode, you need to create a block C Function, specify a name for it in the parameter Function name, and then save this block to user library
. After that, the block can be added to the model in several places, where it will retain access to common data and functions. As a result, the simulation creates only one shared library and one set of functions that is used by all instances of the block. C Function.
When generating code for blocks with the same name in the parameter Function name only general functions are created. init, step and term, and the code from the tab SharedCode it is added to the project once. This simplifies code management and provides access to shared data across all instances of the block. C Function.
Operating variables
Tab Work Variables the block C Function allows you to set static working variables that remain specific to each instance of the block, even if several blocks have the same parameter value. Function name. These working variables are similar to parameters, but differ in the ability to change their values from the source code of the block during execution. This approach allows you to store and modify data that is used only within a specific instance of the block.
For each working variable on the tab Work Variables specified Name and Size in bytes. Inside the source code, these variables are accessible via a pointer to the structure. Work, which is present in the interfaces of all three block functions: step, init, and term. Example:

Here, the alignment is used in the source code, where the macro is_aligned checks whether the variables are aligned correctly. Variables work_variable1 and work_variable3 predefined on the tab Work Variables and are available through the structure Work.
Working with arrays
When data is transferred from Julia to C code, it is important to take into account the differences in indexing and array structure. This is relevant for multidimensional arrays that can be transposed (rearrange the order of the indexes) during transmission. The following are the main points to consider when working with arrays.:
_ Learn more about working with arrays_
-
One—dimensional arrays - when transferring a one-dimensional array from Julia to C, the array retains its structure. Indexing in C starts with
0therefore, when accessing the array elements, this shift must be taken into account. For example, if the block parameter C Function a one-dimensional array is written as a =[1, 2, 3], then in the C language:# include <stdio.h> int main() { int a[3] = {1, 2, 3}; printf("%d", a[2]); // Выведет 3 return 0; }In this example, to get the value of an array element with the index
2An address is being useda[2], which will return the value3. -
Multidimensional arrays — multidimensional arrays can change the order of indexes when transferring from Julia to C. In C code, the array will be transposed, which means that when accessing the elements of the array, you need to take into account the change in the order of the indexes. For example, there is an array in Julia:
a = [1 2 3; 4 5 6] # Julia interprets this as a 2x3 matrix 2x3 Matrix{Int64} 1 2 3 4 5 6 # accessing an array element a [1,2] 2Then in C:
#include <stdio.h> int main() { int a[3][2] = { {1, 4}, {2, 5}, {3, 6} }; printf("%d", a[1][0]); // Outputs 2 return 0; }Array
[1 2 3; 4 5 6]in Julia it becomes an arraya[2][3]in C, where the first index points to a row and the second to a column. In this example, in C, to get an element with the value2indexing is useda[1][0]. Therefore, the array in C actually stores the elements of the transposed Julia array, which must be taken into account when accessing the elements.
Indexing can also change when working with three-dimensional and more complex arrays. For example, if the array in Julia has dimension [2, 3, 4], then in C it will be represented as [4][3][2]. This must be taken into account when accessing array elements in C code.
An example of a three-dimensional array in Julia:
a = reshape(1:24, 2, 3, 4)
[:, :, 1] =
1 3 5
2 4 6
[:, :, 2] =
7 9 11
8 10 12
[:, :, 3] =
13 15 17
14 16 18
[:, :, 4] =
19 21 23
20 22 24
println(a[1, 2, 3]) # Outputs 13
V s:
#include <stdio.h>
int main() {
int a[4][3][2] = {
{
{1, 4},
{2, 5},
{3, 6}
},
{
{7, 10},
{8, 11},
{9, 12}
},
{
{13, 16},
{14, 17},
{15, 18}
},
{
{19, 22},
{20, 23},
{21, 24}
}
};
printf("%d", a[2][1][0]); // Outputs 13
return 0;
}
|
The general rule for converting indexes from Julia to C is to change the order:
This means that the indexes in C are written in reverse order. |
Working with tires
In the block C Function Bus operation is supported both on the input/output ports and in the block parameters. The features of setting up and using tires are described below.:
Buses on the ports
To set the bus on the input or output port:
-
In the Type parameter of the corresponding port (Ports tab), select the type
BusSignal. -
After that, the parameter Input bus type (or Output bus type) will automatically take the form:
BusSignal((), Tuple{}, ())Here you should specify:
-
The type of bus is explicit, for example:
BusSignal((:s1, :s2), (Float64, Int64), ((), ()))where
:s1,:s2— signal names;(Float64, Int64)— their basic types;), (— their dimensions. -
Or the name of the variable containing the description of the bus defined in the Engee workspace (in the window variables) or generated by the C/Julia code.
-
|
If required, you can specify the bus name with the fourth argument.:
BusSignal:s1,:s2), (Float64, Int64), ((), (, :MyBus).
|
Tires in the parameters
If the bus is used as a parameter:
-
In the Value field, set the bus as a named tuple, for example:
(s1 = 4.4, s2 = (b1 = 5, b = 6)) -
If the parameter is called, for example,
p, then in C code , elements can be accessed as fields of a structure:output1 = input1 * (p.s1 + p.s2.b1);
Main
Number of input ports — number of input ports
+
1 (default)
Details
Defines the number of input ports of the block. Parameter Value Number of input ports it will correspond to the number of input ports.
Number of output ports — number of output ports
+
1 (default)
Details
Defines the number of output ports of the block. Parameter Value Number of output ports it will correspond to the number of output ports.
Function name — ID of a common code for several blocks
+
no (by default)
Details
Specify a string value to combine the block code. C Function. Blocks with the same name in the parameter Function name They will use a common code and exchange data through the source code tab. SharedCode the block C Function.
If you leave the field empty, then the selected block C Function it will remain offline (by default).
|
In the parameter Function name can be specified as an absolute path (starts with You can also specify only the file name: in this case, Engee first searches for the file in the current directory, and then in the search paths described in Engee Path Editor. |
Sample time — the interval between the calculation steps
+
-1 (default)
Details
Specify the interval between the calculation steps as a non-negative number. To inherit the calculation step, set this parameter to −1.
Ports
Entrance
Input port 1 — input port No. 1
+
scalar | vector | the matrix
Details
To operate the input port, fill in/configure the following fields:
-
Label is the name (text label) of the input port. By default, the name cell is not filled in (the name is not specified).
-
Variable name is the name of the input port variable in the source C code. By default
input1./* This code is called at each step of the model calculation. */ output1 = input1 * param1;where
input1— name of the input port variable;output1— name of the output port variable;param1— the name of the parameter variable. -
Type is the data type of the input port variable in the C source code. The data type of the variable must match the data type of the signal. Basic C data types are supported:
double (by default)|float|int8_t|uint8_t|int16_t|uint16_t|int32_t|uint32_t|int64_tuint64_t|int128_t|uint128_t|bool|BusSignal -
Size — the dimension of the input signal
1 (default).The Size field uses Julia notation, where size is written as a tuple, for example, (2, 3, 4). However, when working with this port in C code, it is necessary to take into account the change in the order of the indexes. This means that if the port is set with the size(2, 3, 4), then in C code the variable will have the dimension[4][3][2]. This rule is similar to how parameters work: the order of indexes in C is transposed compared to Julia.
Output
Output port 1 — output port No. 1
+
scalar | vector | the matrix
Details
To operate the output port, fill in/configure the following fields:
-
Label is the name (text label) of the output port. By default, the name cell is not filled in (the name is not specified).
-
Variable name is the name of the output port variable in the source C code. By default
output1./* This code is called at each step of the model calculation. */ output1 = input1 * param1;where
output1— name of the output port variable;input1— name of the input port variable;param1— the name of the parameter variable. -
Type is the data type of the output port variable in the C source code. The data type of the variable must match the data type of the signal. Basic C data types are supported:
double (by default)|float|int8_t|uint8_t|int16_t|uint16_t|int32_t|uint32_t|int64_tuint64_t|int128_t|uint128_t|bool|BusSignal -
Size — the dimension of the output signal
1 (default).The Size field uses Julia notation, where size is written as a tuple, for example, (2, 3, 4). However, when working with this port in C code, it is necessary to take into account the change in the order of the indexes. This means that if the port is set with the size(2, 3, 4), then in C code the variable will have the dimension[4][3][2]. This rule is similar to how parameters work: the order of indexes in C is transposed compared to Julia.
Parameters
Number of parameters — specify the number of parameters
+
1 (default)
Details
The number of parameters used in the block.
Parameter 1 — defines a parameter as a variable
+
1 (default)
Details
Defines a parameter as a variable to be used in the source code:
-
Name — default parameter name
param1. It can be changed. The names of the new parameters are calledparam2and then ascending. -
Value — the default value of the parameter
0. It can be changed. The value of the new parameters is also zero by default.
To use the parameters, go to the block code editor. C Function (Edit the source code) in the tab OutputCode and enter the name of the required parameter. By default:
/* This code is called at each step of the model calculation. */
output1 = input1 * param1;
where input1 — input signal variable; output1 — variable output signal.
Work Variables
Number of work variables — number of working variables
+
0 (default)
Details
The number of working variables used in the block to store data specific to each instance. The working variables are available through the structure Work and can be used in functions init, step and term for writing and reading data.
Each variable is set with parameters Name and Size in bytes, which allows you to store various types of data. If a variable is assigned a larger size, then it can be used as an array.
Work Variable 1 — defines a working variable
+
1 (default)
Details
Defines a work variable to be used in the source code:
-
Name — default parameter name
work_variable1. It can be changed. The names of the new working variables are calledwork_variable2and then ascending. -
Size in bytes — the amount of memory allocated for the variable, in bytes. This parameter allows you to specify the size of a variable, whether it is a scalar or an array.
For variables to work, you need to go to the block code editor. C Function (Edit the source code) in the tab OutputCode and enter the variable name.
Build options
Source files — connecting source code files
+
no (by default)
Details
It is used to connect additional source code files. Must include the path and file name along with the extension. For example:
/user/project/src/example.c
Include directories — determining the path to the header file directory
+
no (by default)
Details
It is used to determine the path to the directory containing the header files. For example:
/user/project/include
Library directories — determining the path to the library catalog
+
no (by default)
Details
It is used to determine the path to the directory containing shared libraries. For example:
/user/project/third_party
Headers — connection of header files
+
stdint.h math.h (default)
Details
It is used to connect header files. Must include the name of the header file along with the extension. For example:
example.h
Defines — definition of additional directives #define
+
no (by default)
Details
Used to define additional #define directives. For example:
defines LOWER=0 UPPER=300 STEP=20
Libraries — connecting libraries
+
no (by default)
Details
It is used to connect libraries. Must include the library name along with the extension. For example:
libexample.so
| The actual data type, as well as the support for possible data types, depends on the user code inside the block. |