Skip to main content

Language reference

The structure of the formula set is discussed in this reference.

Structure of the formula set

A formula record represents a string or a direct concatenation of several consecutive strings and consists of statements and comments.

An instruction consists of various terms, which in turn can be constant numerical values or character strings, accesses to variables, operators with their operands, function calls with parameters, bracketed terms and structures, or further nested terms. Statements are always terminated with a ;.

All terms always process and produce time-stamped data, which is typical for the smartCORE. This data is calculated with each other in the correct time, whereby an output value is calculated for each input time stamp of the data streams involved in a term. The signal curve between two time stamps is assumed to be constant with reference to the older value in the time interval, i.e. the older value is valid up to the time stamp of the younger value. Further processing of the data can therefore only take place if all the input values of the term involved also have valid values at a point in time. The valid time range for the calculation ends for a data stream with the most recent time stamp. The following table shows an example of two time-stamped data sources A and B and a calculation:

TimeABA + B
1(not defined)10(not defined)
2515
32025
53035
8838
1394049
26545
27242
30(pending)50(pending)

This means directly that for data that is only provided infrequently or is no longer provided at all, e.g. due to a system component being switched off, all calculations that depend on it are initially only processed up to this most recent timestamp and then blocked there. If the most recent data is more than inputTimeoutS seconds in the past, the values are forcibly continued with this time offset so as not to completely block further calculations and prevent internal buffer overflows.

The parameter evaluationTimeMs only defines the time interval in ms in which the interpreter processes all data points available at this point in time across all terms. This is associated with a certain "reactive power". For this reason, the interval should be chosen to be at least long enough so that data can be processed in each cycle, but preferably also short enough so that the number of individual data points remains within a manageable range (approx. < 100). For example, if you only have data sources that provide new values every 2 seconds, you could set evaluationTimeMs to 5000 ms. For data sources that deliver their data points at 1 kHz, an evaluationTimeMs of 100 ms makes sense. A shorter cycle time also makes sense if the results of the formula set are to be used to implement control tasks in the process.

As the data is processed at the correct time, the order of the expressions in the formula set is ultimately irrelevant. However, for reasons of readability, it is recommended that you only use variables that have been assigned beforehand.

Comments

Comments are - as in the programming languages C and C++ - either started with a // and then end automatically at the end of the line, or they are started with /* and ended with */. The notation /* ... */ can be used nested as with a bracket expression.

// The comment starts with "-here no string and [-here no matrix

s1 = "this is a text"; // from here comment
s2 = "here we go again";

// Helpful marker for complex data structures
Tensor = [ /* Slice 1: */ [ [111, 112]
, [121, 122] ]
, /* Slice 2: */ [ [211, 212]
, [221, 222] ]
, /* Slice 3: */ [ [311, 312]
, [321, 322] ] ];

/* This section is only for commissioning
T1 = 12.34 * Tensor;
*/

The character string // is not taken into account if it is in a character string marked with "..."`` or '...'or within a/* ... */comment. In the example, the assignments toaandeare not interpreted, but the assignment toz``` is.

/* In the block comment is // ignored */
s1 = "This is a // text and neither a comment nor a formula: 1+1";
/*
a = b + c;
e = f * g;
// */ z = b / g;

The comment start and end marks /*...*/ are not taken into account if they are noted after a "visible" //. In the example, the comment block itself is now commented out, the assignments to a and e are executed, but no longer the assignment to z.

// In the line comment /* and */ are ignored
// /*
a = b + c;
e = f * g;
// */ z = b / g;
note

If a block comment /*...*/ that has been started is not closed, this is displayed as an error.

Identifiers and variables

The following conventions apply to the names of variables (signals, channels)

  • the first character must be from the group a-z, A-Z or _
  • all subsequent characters must be from the group a-z, A-Z, 0-9 or _
  • upper and lower case is differentiated
  • all characters not mentioned, e.g. umlauts, special characters, brackets, mathematical symbols, spaces, are not permitted!

All identifiers are case-sensitive, i.e. "varIABLE", "Variable" and "VARiable" are three different identifiers.

It is possible to include data from existing smartCORE channels in the calculation by specifying the channel name. If smartCORE channels whose name violates the previous rule need to be accessed for this purpose, the following notations can be used

$'Some + strange.å =channel ů /name!'
$"Some + strange.å =channel ů /name!"

The same applies to channels that are to be returned to the smartCORE and require special naming.

caution

An assignment may only be made to a variable in exactly one place. Variables without a local assignment are searched for as a data source in the context of the smartCORE. This also applies to variables that represent secondary outputs of a function block and are defined with the properties.

Constants

The following identifiers are available as constants in the context of the formula text:

IdentifierValueType
truetrue<bool>
ontrue<bool>
yestrue<bool>
hightrue<bool>
falsefalse<bool>
offfalse<bool>
nofalse<bool>
lowfalse<bool>
pi3.14159265...3.14159265...<dbl>
magic4242<uint>
j(0,1)(0, 1)<cxFlt>

If there are variables in smartCORE with one of these names, the special notation for the variables $'...' (see identifier) can ensure differentiation. Otherwise, the use of the identifier as a constant has priority.

Instructions

The instructions supported by the calculation rule include the following categories

  • arithmetic-logical operators
  • mathematical-trigonometric functions
  • temporal filter functions (e.g. differentiation and integration of functions)
  • numerical algorithms (e.g. FFT, decomposition of matrices, interpolation, prediction)
  • Sampling of measurement data
  • Counting functions and timers
  • Selection functions, bit-oriented functions and functions for converting data types (casts)
  • Support for sequences of expressions and assignments
  • Text (string) functions

Conditional execution, loops

Due to the continuous processing of flowing, time-stamped data, each term can be understood as an electrical component or filter in an electrical circuit. This makes the implementation of classic control constructs, which influence the flow of the formula text, pointless. For this reason, the following language elements or their variants cannot be found in the calculation module:

  • IF-THEN-ELSE
  • WHILE-DO
  • REPEAT-UNTIL
  • FOR-TO-DO, FOR-EACH
  • SWITCH-CASE

However, there are various functions that can be used to select from different signal sources, for example. This would correspond to a relay or multiplexer in circuit technology.

Other functions allow values to be saved at a specific point in time or changes in values or edges to be detected.

For similar reasons, there is currently no implementation for user-defined procedures or functions.

Functions to support sequences of expressions and assignments

In the formula set, we distinguish between time-independent and time-dependent instructions.

Time-independent execution

Each statement within the formula set must end with a semicolon (;). It may, e.g. for better readability, be spread over several lines and indented with spaces or tabs. Several statements can be specified within one line, separated by a semicolon.

Time-dependent sequencing of expressions

It is also possible to execute expressions in a secured sequence, e.g. to create a parameter list for functions. For this purpose, these expressions must be separated from each other by a comma (,).

Data types and type conversion

Data types

The Math module selects a suitable data type for the calculation in order to be able to perform the desired operations with the best possible representation. The types are as follows

Data typeNameDescriptionBit lengthMinimumMaximum
boolean<bool>truth value1false, no, off, lowtrue, on, yes, high
unsigned integer<uint>unsigned integer64018446744073709551615
integer<int>signed integer64-9223372036854775808+9223372036854775807
Lowest resolutionValue range
double<dbl>double precision floating point number64±5.0E-324±1.7976931348623157E+308
complex float<cxFlt>complex floating point number with single precision (*)2x 32±1.175494E-38
per Re-/Im component
±3.402823E+38
per Re-/Im component
string<str>text, UTF-8N/AN/AN/A

As a rule, you do not have to worry about the type; when the data is transferred to the smartCORE, it is converted to the appropriate data type.

info

In this function description, the data type is usually placed in angle brackets, e.g. <bool> or <str>.

Automatic type conversion

When processing numerical data using binary operators, the following automatic type conversion takes place with reference to the operator types

booluintintdblcxFltstr
boolbooluintintdblcxFltstr
uintuintuintintdblcxFltstr
intintintintdblcxFltstr
dbldbldbldoudblledblcxFltstr
cxFltcxFltcxFltcxFltcxFltcxFltstr
strstrstrstrstrstrstr

So that as long as possible

  • Boolean values also only true/false
  • Preserve the properties of uint values for bit operations
  • preserve the sign when using int values
  • get the precision of floating point numbers
  • the complex number space available

Special features for bool

  • When converting to <bool>, the comparison to not equal to 0 implicitly applies in order to arrive at the truth value.

  • When converting <bool> to a numeric type, false becomes 0 and true becomes 1.

  • When adding two Boolean values, the conversion is made to <uint>.

  • When subtracting two Boolean values, the conversion is made to <int>.

  • When calculating data fields of type <bool>, the result is always <int>.

Special features for uint

  • When subtracting the second sign of loose <uint> - <uint>, the result is <int> if the second operand is greater than the first. This avoids the usually completely nonsensical, unintentional overflow in sizes of 2^64.

  • When calculating data fields of type <uint>, the result is always <int>.

Manual type conversion

It is also possible to perform a manual type conversion using the following functions

FunctionDescription
bool(x)Conversion of the argument x to bool
uint(x)Conversion of the argument x to uint
int(x)Conversion of the argument x to int
dbl(x)Conversion of the argument x to double
str(x)Conversion of the argument x to string

Numerical types

Numerical values can also be specified in different number systems directly in the formula text and the configuration objects for the properties of functions. Examples of notations are

  aBool = true; // alt.: false, on, off, yes, no, high, low
anUnsignedInt = 42; // alt.: 42u
aHexUInt = 0x2A; // alt.: 2Ah
anOctUInt = 052; // alt.: 52o
aBinUInt = 0b10'1010; // alt.: 101010b
aSignedInt = -42;
aDouble = 3.14159; // alt.: 1.6022e-19, pi, !! decimal-point
aComplex = 2.7+1.0*j; // alt.: cx(2.7, 1.0)
Group separator

For unsigned (<uint>) or signed (<int>) integers, the individual digits can be divided into groups by inserting a single quotation mark '. This character plays no role in the interpretation of the numerical value, but simplifies reading. It must not be placed at the beginning or end of a number.

aHexUInt = FFe7'0815'337Eh; // 0815 is not a string here!
aBinUInt = 0b1011'0110'1010;
aLargeInt = 1'000'000'000;
Sign and negation

By placing a - operator in front, the negative value is formed for each number format (2's complement).

Prefixing the + operator is permitted, but has no function.

The ~ operator inverts the integer at the bit level of a <uint> (example: ~0b1010'0011'1100 -> 0b1111...1111'0101'1100'0011).

Hexadecimal number format

A number is represented in the hexadecimal system (base 16) either by prefixing it with 0x or 0X or by adding the letter h. The possible digits are 0 ... 9, a .. f or A ... F and the group separator.

note

The specification of a number in the hexadecimal system is always interpreted as unsigned, but can be negated or inverted using the operators - or ~.

Octal number format

A number is represented in the octal system (base 8) either by prefixing it with 0 (zero) or by adding the letter o. The possible digits are 0 ... 7 and the group separator.

note

The specification of a number in the octal system is always interpreted as unsigned, but can be negated or inverted using the operators - or ~.

Binary number format

A number is represented in the binary system (base 2) either by prefixing it with 0b or 0B or by adding the letter b. The possible digits are 0 ... 1 and the group separator.

note

The specification of a number in the binary system is always interpreted as unsigned, but can be negated or inverted using the operators - or ~.

Character strings (string literals)

Constant character strings are enclosed within single ' or double " quotation marks. If a character string itself contains a ' or ", this must be preceded by an \ escape character. Examples:

s1a = '17" (inch)';
s1b = "17\" (inch)";
s2a = "Did it work?";
s2b = 'Did\'s work?

In general, special characters in character strings that are used, for example, in the interpretation or generation of serial device protocols, can be represented by the following escape sequences:

characterASCII nameASCII valueescape sequence
Newline (Linefeed)NL (LF)10\n
Horizontal tabHT9\t
Vertical tabVT11\v
BackspaceBS8\b
Carriage returnCR13\r
FormfeedFF12\f
AlertBEL7\a
EscapeESC27\e
Backslash\92\\
Question mark?63\?
Single quotation mark'39\'
Double quotation mark"34\"
Character from octal codeNNN = 000 ... 377
(octal digits)
\oNNN
\o{N...}
Character from decimal codeNNN = 000 ... 255
(decimal digits)
\dNNN
\d{N...}
Character from hex codeNN = 00 ... FF
(hex digits)
\xNN
\x{N...}
UTF characer(s) from decimal codeUTF8-MultibyteNNNN = x0000 ... xFFFF
x00000000 ... x0010FFFFFF
\uNNNNN
\UNNNNNNNNN
\u{N...}

The characters are always encoded internally in UTF-8 (UTF-8 - Wikipedia).

Arithmetic-logical operators

This section discusses the arithmetic-logic operators including their ranking and associativity.

We describe an operator ° as left-associative if the following applies

a ° b ° c = (a ° b) ° c

and accordingly as right-associative if the following applies

a ° b ° c = a ° (b ° c)

An operator * has a higher precedence (precedence, binding strength) than operator + if the following applies regardless of the associativity of both operators

a + b * c = a + (b * c)

If the binding of terms is not immediately obvious, it is recommended to enforce the intended evaluation sequence by using ```(...)`` abundantly. Especially with logical operators or comparison operators, the precedence is not always familiar. For complex expressions, the use of several consecutive lines and a suitable alignment of the terms for structuring is also recommended.

res = A && (B || (C && D) || (E && (F || G))); // Variant 1
opt = A && ( B // optional comment
|| (C && D) // optional comment
|| (E && ( F // optional comment
|| G) // optional comment
)
); // Variant 2

Operators

The following table contains the available operators, sorted by ascending precedence. All operators listed here work on the previously introduced data types. Separate functions are available for the bit-oriented logical links and operations.

OperatorAssignmentAssociativityDescriptionExampleResult type
;lefttime-independent executed sequence of commandsy = 7; x = 4void
,leftSequence of individual expressionsa = 4, b = 6, a + blike last expression
+=YESrightcompound assignment regarding additiona += 17automatically
-=YESrightcompound assignment regarding subtractiona -= bautomatic
*=YESrightcompound assignment regarding multiplicationa *= bautomatic
/=YESrightcompound assignment regarding divisiona *= bautomatic
%=YESrightcompound assignment regarding modulob %= 4automatic
=YESrightassignmenta = 23 * bas assigned expression
? :rightternary operator if-else(a > 23) ? 12 : -50like resulting body
||leftlogical disjunction (OR)a || b<bool>
&&leftlogical conjunction (AND)a && b<bool>
^^leftlogical antivalence (XOR)a ^^ b<bool>
==leftequalitya == b<bool>
!=leftinequalitya != b<bool>
<leftless thana < b<bool>
<=leftless than or equal toa <= b<bool>
>leftgreater thana > b<bool>
>=leftgreater than or equal toa >= b<bool>
+leftadditiona + bautomatic
-leftsubtractiona - bautomatic
*leftMultiplicationa * bautomatic
/leftdivisiona / bautomatic
%leftmoduloa % bautomatic
^rightexponentiationa ^ 0.333<dbl>
!rightlogical inversion (NOT)!(a && b)<bool>
~rightbit-oriented inversion~a<uint>
+rightpositive sign6 / +aautomatic
-rightnegative sign9 * -aautomatic

Assignment operators

important

An assignment may only be made to a variable at exactly one single point in the formula text. Variables without a local assignment are searched for as a data source in the context of the smartCORE.

For assignment operators, a variable (x, y, ...), an element of a previously assigned matrix (V[2], M[0,2], ...) or the initializer for a discretely sampled variable (k@0, ...), in which the result is stored, must be on the left-hand side. The result of the expression remains the assigned value. This also enables the sequencing of assignments or the use of an assignment within an expression.

Operators for data fields

If the operators +, -, * and / are applied to data fields (vectors, matrices, tensors), the usual mathematical rules regarding the dimension of the operands and the resulting dimension of the result apply. The following rules apply:

  • For addition and subtraction, the data fields involved must have the same dimension. The operation is performed on the elements with the same indices.

  • A data field can be multiplied by any scalar: DF * s or s * DF. The operation is performed for each element.

  • A data field can only be divided by one scalar: DF / s The operation is performed for each element.

  • For the multiplication, the first data field must have as many columns as the second data field has rows. The result contains as many rows as the first data field and as many columns as the second data field.
    As no explicit distinction is made here between row and column vectors for vectors, the following rules apply:

    • The product of two vectors of the same length is always the scalar product.

    • For the vector product of 2- and 3-component vectors, use the function Cross(v1, v2).

    • The product of vector and matrix reads the vector as a row vector.

    • The product of matrix and vector reads the vector as a column vector.

Matching of data fields

Two data fields are exactly the same if they match exactly in terms of data type, dimension and all elements. An automatic type conversion does not take place before the comparison. Two data fields are not equal if they are not equal with regard to the previous definition.

No other comparisons are available.

Braces

The following pairs of brackets are also recognized in nested expressions. Each opening bracket must be closed correctly in the correct order.

BracketingMeaningExample
( ... )Bundling in the evaluation sequence, grouping of function parameters(a+b)/2, max(a, b, 50)
[ ... ]Reserved for representation of vectors, matrices and tensors and indexing of variables, indexing always counts from 0...(N-1),
for vectors : V[<idx>], row/column vector is not distinguished
for matrices: M[<row>, <col>]
for tensors: T[<slice>,<row>, <col>]
a = [1, 2, 3], b = [[1.1, 1.2], [2.1, 2.2]], a[1] + b[0,1]
{ ... }- Bracketing for JSON objects that can be used to parameterize function blocks.
As these can become very extensive, it is recommended to refer to an external JSON resource via a link $ref: '<path>' which is collected in a separate container of the calculation module.
timer({ interval: 60 })
? ... :The expression between ? and : is implicitly surrounded by (...).

Functions

Functions for the Math module are provided by the plugin itself, in future also via other theme plugins for the Math module, and are generally implemented in C++.

Function call

Each function consists of a function name that follows the usual identifier syntax

  • the first character must be from the group a-z, A-Z or _
  • all subsequent characters must be from the group a-z, A-Z, 0-9 or _
  • upper and lower case is distinguished

followed by a parameter list that contains a comma-separated list of individual terms in ( ... ).

When describing complex function blocks, the parameters of the function are also referred to as inputs and the return value of the function as output.

Function call as circuit symbol

Namespace identifiers can be prefixed to the function name with a '.'. Otherwise, the identifiers for namespaces follow the same rules.

Examples of function calls:

   f1 = functionNoParam();
f2 = functionOneParam(x);
f3a = functionOptionalParam(x,a);
f3b = functionOptionalParam(x,a,b);
f3c = functionOptionalParam(x,a,b,c);
f4 = functionWithConfig(x,y,z, {...});

For the parameters, a distinction is made between mandatory and optional parameters (inputs), as well as a configuration object for the function properties as the last element. The meaning of the parameters can vary depending on the number of parameters used; this can be found in the description of the function.

To avoid redundancies and the associated errors, placeholders ... are used in the description of the function. As a rule, all variants with optional parameters are listed, as well as an additional entry in which only the configuration object is described:

f1 = aFunction(x);
f2 = aFunction(x, y);
// Optional configuration for all variants
fx = aFunction(..., { property1: <type>
, property2: <type>
});

If the configuration object is mandatory in a variant, it is also listed directly in this variant as a placeholder.

f1 = bFunction(x, {...}); // Configuration mandatory
f2 = bFunction(x, y, z); // Configuration optional
// Configuration for all variants
fx = bFunction(..., { property1: <type>
, property2: <type>
});

Each use of a function in the formula text is represented by a separate instance in which, for example, states or aggregations can be saved from calculation cycle to calculation cycle. Examples of this are

  • Counters
  • totalizers, integrators
  • Filters
  • Hysteresis
  • Logic elements with memory function
  • Functions with time reference

Functions are generally executed and generate an output value if valid values are available for all parameters and at least one parameter has a new timestamp. In addition, some functions are also executed if an (internal) time condition is fulfilled.

Certain functions, such as intergrate() or stopwatch(), mark their output data in such a way that linear interpolation can also be performed between two specific data points of subsequent calculations. This improves the quality of the calculation, as it is known here that a constant input signal will lead to a linear output signal.

Functions can also place additional output values in the data stream independently of the timestamps of the parameter values or the processing cycle, e.g. if a signal must be reset when a timer expires or the form of the output signal is defined by the function itself. Examples of this are the functions posedge() and negedge(), which each write a pulse with a duration of 1 nano second to the output data stream when an edge is detected.

Configuration of the function properties

The behaviour of certain functions can be set, optimized or changed using properties. These properties are transferred to the function in a configuration object {...} in the last position of the parameter list.

Compared to the JSON standard, the implemented syntax allows some simplifications but also useful extensions, e.g. to clean up the JSON representation in the "math" property from too many \ escape sequences:

  • Names of properties or ENUM tags do not have to be enclosed in quotation marks
  • The use of quotation marks "..." and '...' for the assignment of strings is equal.
  • For integer number values, all notations, such as 0x... or 0... can be used for the input.
  • Some properties also allow certain ENUM tags as an alternative to specifying a numerical value, e.g. to switch off the corresponding function ('off') or to set default values ('def', 'auto', ...).
  • Comments in both notations // ... and /* ... */ are permitted

The following types are provided for properties:

TypeValuesExample
<bool>true, on, yes, 1
false, off, no, 0
edge: yes
<uint>integer, positive values
also in 0x- (hex) or 0- (octal) notation
range: 0x7F
<int>integer values with signmin: -10
<dbl>Floating point numbers (decimal point!) also in exponential notation 1.35e-22delay: 13.45
<cxFlt>complex floating point numberpole: (-5, 0.8)
<str>String / text, quotation marks only mandatory if spaces, commas or special characters are included.storage: 'total_km'
<var>Variant, can assume all previously mentioned typespreset: 0x2F
<enum>Certain enumerating tags, see description of the functionmode: peak2
off/<type>Alternative from enum tags or a regular type <type>lower: off,
upper: 12.6
[<type>]Comma-separated vector with data of type <type>map: [1,2,4,8]

Using the reserved keyword $ref, the content of the configuration can be obtained completely from a resource. This can be a configuration in the simplified JSON format described above or any text or even binary content, provided the function requires and supports this.

   f = someConfigExpectingFunction({$ref:'someNamedResource'});
important

The configuration is basically constant over the runtime. A calculation of the property values is not permitted. A link to variables in the formula set is not possible. Some functions have optional parameters that allow settings such as limit values to be changed dynamically.

The properties listed in the documentation are optional unless they are marked as mandatory.

Persistent status information

Certain functions that are preferably used for the aggregation of signals or events can save their internal state via the persistent channels of the smartCORE and, after a restart of the software, build on the previously saved state and continue the calculation. Examples of this are

  • Counters
  • Integrators
  • Classifications

For this purpose, the name for a storage location is specified in the JSON configuration area of the corresponding function using the storage property. The content of this automatically generated and managed variable cannot usually be interpreted by the user or other smartCORE function units. It is not permitted to use the same storage location in different places in the formula text.

tip

If a state with a reference to a physical quantity is persisted in the memory, it is recommended to append an identifier for its unit to the name (e.g. 'totalDistance_km') in order to simplify the readability and subsequent maintenance of the formula text.

caution

The contents of the persistent memory can only be accessed again using the same function.

In the following example, a meter reading is persisted:

  f = someCounterFunction(someCondition, {storage:'some.counter.variable'});
note

All persisted data is periodically stored as a BLOB in an SQLite database. Entries in this database are not automatically removed.

Preprocessor directives

Preprocessor directives can be used to influence functions of the Math Module Compiler, the processing of variables and the execution of the code. The preprocessor directives must be placed at the beginning of a line in the formula text and end with the first line end that is not hidden in a bracket level. Comments can be inserted according to the above rules.

The directives are identified by a # character followed by an identifier, which is formed according to the rules mentioned above. Which parameters follow the directive and how they are formatted depends on the function. Possible formats are, for example:

#directiveA <parameter>
#directiveB <name>[<parameter>](<parameter>){<code>}
#directiveC <key>, <key>=<value>, ...

The Preprocessor directives are compiled in this document.