Code Blocks and Looping
Code Blocks from Expression Engine perspective behave as functions – i.e. can define a return value. These blocks consist of code lines and can use internal variables. Access to internal variables is very fast and a single variable can contain any data type that a standard expression value can hold (simple types, arrays, etc.). Internal variables do not need type definitions. Every internal variable starts with an @ character and can be a combination of alphanumeric characters. The default value for all internal variables is an “Empty” value. Internal variables defined in the main code block can be used by inner code blocks or code lines sections.
Main code blocks have two basic sections – variable declarations (optional), and code lines section. Each data block internally defines a return variable with a special, well-known name - @@.
DECLARE
Internal variable declaration section
(e.g. @myvariable; @mysecondVariable = 10;)
BEGIN
Code section with code lines…
END
When the declare section is not specified, then the only available internal variable is the return value @@. The variable declaration section can define any number of internal variables. Each variable can be optionally initialized. The variable declaration line ends with a semicolon character.
@myVariable;
@myInitializedVariable = 10;
As was mentioned earlier, multiple code blocks can be used side-by-side inside an expression:
BEGIN SET @@=5; END + BEGIN SET @@=7; END
Is equivalent to
5 + 7
These examples are used to demonstrate code blocks behavior as functions and how to specify a return value inside a code block. In this example, both code blocks are independent, thus cannot share their internal variables. To be able to share variables between separate code blocks, one upper code block is necessary:
DECLARE
@a = 5; @b = 7;
BEGIN
SET @@= BEGIN SET @@=@a; END + BEGIN SET @@=@b; END
END
Code Lines
Each code line starts with a keyword and ends with a semicolon character.
SET STATEMENT
This is used to set internal variables. The following is the SET syntax:
SET @variable_name = value;
Where variable_name specifies the internal variable declared in the declare section and the value can be any standard expression [combination of functions, operators and variables (internal or external)].
Examples:
SET @myvar = 10;
SET @myvar = @myvar + 1;
SET @myvar = {{my OPC Tag 1}} + {{my OPC Tag 2}};
SET @myvar = tonumber({{my OPC Tag 1}}) * @myvar2 + {{my OPC Tag 2}};
SET @myvar = IF {{my OPC Tag 1}} > 0 THEN {{my OPC Tag 1}} ELSE 0;
EXIT STATEMENT
This command allows for an exit code block earlier than in the end of the code block. It can be used as a simple EXIT command, where it makes sense in combination with an IF statement or conditional exit with WHEN part specified:
EXIT WHEN boolean_condition (expression);
or
EXIT;
The Exit command is not propagated to upper code blocks – i.e. Exit exists from the existing code block only.
Examples:
EXIT WHEN @a > 0;
IF @a > 0 THEN EXIT;
EXIT WHEN {{my data tag}} > 1 && @a < 0;
FOREACH STATEMENT
Simple loop designed to iterate through arrays. Syntax is:
FOREACH counter_variable IN array_value
LOOP
/* add code lines here */
END
where counter_variable is an internal variable, which is declared by FOREACH statement – i.e. doesn’t need to be explicitly declared. This variable is known inside the loop body only and contains a current array element. The array_value is any expression (functions, operators, etc. that can return an array. In case it returns a simple value (non-array value), it is converted by the expression conversion mechanism to a single element array.
Looping can be stopped earlier (if needed) by EXIT statement.
Examples:
/* simple sum function */
DECLARE
@sum = 0;
BEGIN
FOREACH @elm IN
array (
{{data:Signals.RampSlow}},
{{data:Signals.RampFast}},
{{data:Signals.RandomFast}})
LOOP
SET @sum = @sum + @elm;
END
SET @@ = @sum;
END
Or without using explicitly declared variable:
BEGIN
FOREACH @elm IN
array (
{{data:Signals.RampSlow}},
{{data:Signals.RampFast}},
{{data:Signals.RandomFast}})
LOOP
SET @@ = @@ + @elm;
END
END
With Exit statement:
BEGIN
FOREACH @elm IN
array (
{{data:Signals.RampSlow}},
{{data:Signals.RampFast}},
{{data:Signals.RandomFast}})
LOOP
SET @@ = @@ + @elm;
EXIT WHEN @@ > 100;
END
END
FOR COMMAND
The FOR loop is always looping between specified numbers and optionally allows for specifying direction and step values. Loop values (from, to, step) are read before looping starts, thus any changes to these parameters via internal variables won’t take any effect to actual loop. Looping can be stopped earlier (if needed) by EXIT statement.
Syntax is:
FOR counter_variable IN [REVERSE] lowest_number..highest_number [STEP value]
LOOP
/* code lines */
END
Where counter_variable is an internal variable, which is declared by FOR statement – i.e. doesn’t need to be explicitly declared. This variable is known inside the loop body only and contains current counter value. The lowest and highest numbers determine the FORloop range. To start looping, the lowest number must contain a lower value than the highest number. When REVERSE is specified, then it starts looping from the highest number and ends in the lowest value. STEP is followed by a numeric value that specifies the increment value used in FOR loop. The default value is 1.
Examples (without code block):
FOR @i IN 1..10
LOOP
/* values in variable @i will be: 1,2,3,4,5,6,7,8,9,10 */
@@ = @@ + @i;
END
/*********************************************************/
FOR @i IN REVERSE 1..10
LOOP
/* values in variable @i will be: 10,9,8,7,6,5,4,3,2,1 */
@@ = @@ + @i;
END
/*********************************************************/
FOR @i IN 1..10 STEP 3
LOOP
/* values in variable @i will be: 1,4,7,10 */
@@ = @@ + @i;
END
/*********************************************************/
FOR @i IN REVERSE 1..10 STEP 3
LOOP
/* values in variable @i will be: 10,7,4,1 */
@@ = @@ + @i;
END
FOR loop can use internal/external variables in place of lowest/highest and step numbers. See full examples below:
/*********************************************************/
DECLARE
@a = 0;
BEGIN
FOR @i IN {{data:Signals.RampSlow}}..{{data:Signals.RampFast}}
LOOP
SET @a =
IF {{data:Signals.RampSlow}} > 50
THEN @a + @i
ELSE @a - @i;
END
SET @@ = @a;
END
/*********************************************************/
DECLARE
@array; @a;
BEGIN
SET @@ = 0;
SET @array = array({{var_a}}, {{var_b}}, {{var_c}});
FOR @i IN 0 .. (len(@array) - 1)
LOOP
SET @a = getat(@array, @i);
IF @a > 0 THEN SET @@ = @@ + @a;
END
END
IF AND IFQ STATEMENTS
IF statements can be used directly in code blocks. Their behavior is very similar to IF used in standard expression text. The only difference is that THEN and ELSE values are not expressions, but either a code line or another code block. The ELSE section is optional.
Syntax is:
IF Boolean_condition THEN code_block [ELSE code_block]
Examples:
BEGIN
IF {{my variable}} > 0 THEN
EXIT;
SET @@ = {{my variable}};
END
/*********************************************************/
BEGIN
IF {{my variable}} > 0 THEN
BEGIN
SET @@ = {{my variable}};
EXIT;
END
SET @@ = “No Value”;
END
CASE-WHEN-THEN STATEMENTS
CASE statements can be used directly in code blocks and their behavior is very similar to CASE statements used in standard expression text. The only difference is that THEN and ELSE values are not expressions, but either code lines or another code block. The ELSE section is optional.
Notes
Loops with eventually infinite end are intentionally unsupported (e.g. while loop). This is because it can quite easily create an infinite loop, which can be very difficult to find (e.g, in a GraphWorX64 display with thousands of expressions). Instead, a FOR statement, in combination with an EXIT statement, can be used. A FOR loop always requires specification of the maximum number of iterations (can be multiples of expected iterations). Loops should be always used with care. They can quite easily create extremely long evaluations (with thousands of evaluation steps), which may eventually block the whole application.
See Also: