LogixNG Reference - Chapter 8

Local Variables

JMRI has memories that can store data. And that's great. But there is a problem with memories. They are global, which means that anyone can change them. Lets assume you want to count something so you create a memory IMCOUNTER. You create a LogixNG that does the counting and everything works fine.

A year passes and you need to count something else. You create a new LogixNG that does the new counting and you need a memory, so you decide to use the memory IMCOUNTER. And your new LogixNG works fine. But then you suddenly realize that something else on the layout stops working. Why? What has happen? Well, the problem is that you use the same memory IMCOUNTER for two different things in two different places. And when that happens, you get into trouble.

LogixNG has a simple solution to this problem, local variables. A local variable is similar to a memory, but it only exists in a very limited context and nothing outside that context can interfere with the variable. The local variable is also transient. It is only available during the execution of the ConditionalNG and it goes away when the execution is finished. This protects the local variable from being changed in an unpredictable way.

Defining local variables

Local variables are created in the ConditionalNG editor. Open the editor of a ConditionalNG and right-click on an action or expression and select Local variables.

Chapter 8 local variable menu item

The local variable dialog window will open.

Chapter 8 local variables dialog

To create a local variable, click on the Add variable button. A new row will be added. Double click in the Name field and give it a name. Select the Type and provide an initial value if desired. The Select menu is used to Delete a local variable and has Move Up and Move Down options if there is more than one variable.

Click on OK to finish. The local variable will be added to the tree. See the editor image below.

Local variable types

Chapter 8 local variable types
None
The initial value will be null.
Boolean since 5.7.5
Contains the boolean value true or false. The state is set or referenced by using the words true and false. The words are case insensitive. Setting an initial value is recommended.
Integer
A whole number such as 123
Floating number
A number such as 123.4
String
A series of characters, such as ABC123def
Array
A list of items. This is implemented using the Java ArrayList. This is similar to the Python List.
Map
A list of kev/value items. This is implemented using the Java HashMap. This is similar to the Python Dictionary.
Local Variable
Use the value from another local variable.
Memory
Use the value from a memory variable.
Reference
Use the value from an indirect reference.
Formula
Use the value from a formula
Script expression
Use a one line script command to set the value of the variable. For example: sensors.getSensor("BlinkSensor"). This gets a sensor object which can then be set active or inactive. For details on accessing LogixNG objects from a script see Chapter 13 - Jython Scripting Support.
Script file
For more complex logic, a script file can be used to set the value. There are sample scripts at the jython/LogixNG directory within the JMRI install location. Note: The results of the script are passed back to LogixNG using variable.set(some_python_variable). This is a Python global variable that is added by the LogixNG scripting support. For details on accessing LogixNG objects from a script see Chapter 13 - Jython Scripting Support.
LogixNG Table
Load the contents of a specified LogixNG table into a variable. The table name is specified in the data column when creating the local variable. For global variables, use the initial value field. Here is an example that creates the local variable and provides access to the table rows and columns:
Chapter 8 local variable table_type

Note: The type of a variable can change based on the last assignment. For example, a local variable with the name index defined as integer would change to string if "XVZ" was assigned to index.

Arraysince 4.25.8

When an Array Local Variable is defined, it will default to empty. Rows can be added to the Array using the add method. The rows can also be defined by providing a value in the data column, in this example 5 rows for Array2.

Chapter 8 local variable array

Some examples of setting the initial Array values. size is another local variable that contains the row count, such as 23.

ExampleResult
Data field is emptyThe array is empty
1212 empty strings in the array
size23 empty strings in the array
12:"Hello world"12 "Hello world" strings in the array
size:"Hello world"23 "Hello world" strings in the array
12:23312 items with the number 233 in the array
size:23323 items with the number 233 in the array
12:43.32312 items with the number 43.323 in the array
size:43.32323 items with the number 43.323 in the array

The rows in the array are accessed by using the row number. The first row has a row number of zero. The last row number is the size of the array minus 1. To set the value of a row: array[1] = value. To get the value: value = array[1].

The following list has the common ArrayList methods using the Java syntax.

The following image shows some of the options for working with an array.

Chapter 8 local variable array sample
Mapsince 4.25.8

When a Map Local Variable is defined, it is empty. Rows are added as shown in the example.

Chapter 8 local variable map

The rows in the map are accessed by using the key for the row. To add a new key/value pair: map{key} = value. The same syntax will replace the value for an existing key. To get the value for a key: value = map{key}.

The following list has the common HashMap methods using the Java syntax.

The following image shows some of the options for working with an array.

Chapter 8 local variable map sample
Log local variables:
  Name: Map1, value: {key=value}
  Name: Map2, value: {new=value, abc=xyz}
  Name: Size2, value: 2
  Name: Size1, value: 1
Log local variables done

Order of local variables

The order of the local variables that are defined in an action or expression matters if you use one variable in the initialization of another. Lets say you define two variables "a" and "b", and that you define "a" to be initialized to the formula "34 * 4" and you define "b" to be initialized to "a * 3". This will work if "a" is defined before "b". You can define both variables in the same action or expression, but "a" needs to be before "b" since "b" uses "a" in its initialization.

Local variables are local

This may seem obvious, but when writing scripts that uses local variables, it's important to remember this. A script can access the symbol table if it has access to the ConditionalNG, but the symbol table is dependent on which action or expression that's currently running.

The scope of a local variable depends on where it is defined in the tree. This a contrived example to show scope behavior.

There are two local variables defined. One at the root of the tree and a second one part way up the first branch.

Chapter 8 local variables scope

The output on the JMRI system console shows the result when the ConditionalNG is executed.

in if trees
WARN  - Log local variables: [JMRI LogixNGThread]
WARN  - Name: rootlevel, value: null [JMRI LogixNGThread]
WARN  - Name: iftree, value: null [JMRI LogixNGThread]
WARN  - Log local variables done [JMRI LogixNGThread]

root level
WARN  - Log local variables: [JMRI LogixNGThread]
WARN  - Name: rootlevel, value: null [JMRI LogixNGThread]
WARN  - Log local variables done [JMRI LogixNGThread]
        

Initialization of local variables

When an action or expression is executed that defines local variables, these local variables are created with an initial value. When you create the local variable, you define what the initial value should be. 'None' means that the value will be 'null'. Reference means that the value will be the value that the reference is pointing at. Formula means the result of the formula.

Debugging local variables

What to do if the ConditionalNG doesn't do the expected? The debugger provides one tool, but another tool is the action Log local variables. Each time it's executed, it prints all the local variables and their values to the JMRI Sytem Console.

Chapter 8 log variables

Options:

Example:

WARN  - Log local variables: [JMRI LogixNGThread]
WARN  -     Name: time, Value: Time is 13:02 [JMRI LogixNGThread]
WARN  - Global variables: [JMRI LogixNGThread]
WARN  -     Global Name: gHour, value: 13 [JMRI LogixNGThread]
WARN  -     Global Name: gMin, value: 2 [JMRI LogixNGThread]
WARN  -     Global Name: timeMap, [JMRI LogixNGThread]
WARN  -         theHour -> 13, [JMRI LogixNGThread]
WARN  -         theMin -> 2, [JMRI LogixNGThread]
WARN  -         theTime -> The map time is 13:02, [JMRI LogixNGThread]
WARN  -     Global Name: timeArray, [JMRI LogixNGThread]
WARN  -         0: 13, [JMRI LogixNGThread]
WARN  -         1: 2, [JMRI LogixNGThread]
WARN  -         2: The array time is 13:02, [JMRI LogixNGThread]
WARN  -     Global Name: null, value: null [JMRI LogixNGThread]
WARN  - Log local variables done [JMRI LogixNGThread]
        

Chapter 9 - Formula

Return to the Reference TOC