530 lines
21 KiB
Plaintext
530 lines
21 KiB
Plaintext
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta name="generator" content="HTML Tidy for HTML5 for Apple macOS version 5.8.0">
|
|
<title>LogixNG Reference - Chapter 9</title>
|
|
<meta name="author" content="Daniel Bergqvist">
|
|
<meta name="author" content="Dave Sand">
|
|
<meta name="keywords" content="jmri LogixNG reference formula">
|
|
<!--#include virtual="/help/en/parts/Style.shtml" -->
|
|
</head>
|
|
<body>
|
|
<!--#include virtual="/help/en/parts/Header.shtml" -->
|
|
|
|
<div id="mBody">
|
|
<div id="mainContent" class="no-sidebar">
|
|
<h1>LogixNG Reference - Chapter 9</h1>
|
|
|
|
<h2>Formula</h2>
|
|
|
|
<p>LogixNG has native support for complex calculations with the tool "Formula". Formula
|
|
supports almost all the Java operators and you can use local variables and functions with
|
|
formula. In many cases, like the action Turnout, you can choose to use formula to get the
|
|
turnout you want to act on, or to get the new state you want to set.</p>
|
|
|
|
<p>Local variables, which are explained in <a href="chapter8.shtml">chapter 8</a>, can be
|
|
used directly in formula. So if you have a local variable <em>index</em>, you can for example
|
|
have the formula <em>"IT" + str(index)</em>, which adds the string "IT" and the value of
|
|
<em>index</em>. This can be useful if you for example want to set all the turnouts IT1, IT2,
|
|
IT3, ..., IT10 to thrown. You can then use the <strong>For</strong> action to iterate from 1
|
|
to 10 and to set each of these turnouts to thrown.</p>
|
|
|
|
<!-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = -->
|
|
|
|
<h3>The Formula expressions</h3>
|
|
|
|
<p>There are three expressions for formula: Analog Formula, Digital Formula and String
|
|
Formula. They all work the same way, except that Analog Formula returns a floating point
|
|
number, Digital Formula returns a boolean value (true or false), and String Formula returns a
|
|
string. The expression Formula can have child expressions, for example reading an analog
|
|
value or reading the state of a sensor. You use the result of the child expressions by using
|
|
the name of the female socket in the formula. So if you have an expression Formula which has
|
|
a child E1 to which an expression Sensor is connected, you can use the result of the
|
|
expression Sensor in the formula by the identifier E1 which points to the female socket and
|
|
its connected expression.</p>
|
|
|
|
<!-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = -->
|
|
|
|
<h3>Operators</h3>
|
|
|
|
<p>Formula supports most of the Java operators. A list of the Java operators, together with
|
|
the priority of them, is on <a href=
|
|
"https://introcs.cs.princeton.edu/java/11precedence/">this page</a>.</p>
|
|
|
|
<p>Currently supported operators are:</p>
|
|
|
|
<div style="margin-left: 2em">
|
|
<table>
|
|
<tr>
|
|
<th>Operator</th>
|
|
<th>Description</th>
|
|
<th>Associativity</th>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>v++<br>v--</td>
|
|
<td>post-increment<br>post-decrement</td>
|
|
<td>not associative</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>++v<br>--v</td>
|
|
<td>pre-increment<br>pre-decrement</td>
|
|
<td>right to left</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>-</td>
|
|
<td>unary minus</td>
|
|
<td>right to left</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>!</td>
|
|
<td>unary logical NOT</td>
|
|
<td>right to left</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>~</td>
|
|
<td>unary bitwise NOT</td>
|
|
<td>right to left</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>* / %</td>
|
|
<td>multiply, divide, modulo</td>
|
|
<td>left to right</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>+ -<br>
|
|
+</td>
|
|
<td>additive<br>
|
|
string concatenation</td>
|
|
<td>left to right</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><<<br>
|
|
>><br>
|
|
>>>
|
|
</td>
|
|
<td>shift left<br>
|
|
shift right<br>
|
|
unsigned shift right
|
|
</td>
|
|
<td>left to right</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>< <=<br>
|
|
> >=</td>
|
|
<td>relational</td>
|
|
<td>not associative</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>==<br>
|
|
!=</td>
|
|
<td>equality</td>
|
|
<td>left to right</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>&&</td>
|
|
<td>logical AND</td>
|
|
<td>left to right</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>^^</td>
|
|
<td>boolean XOR</td>
|
|
<td>left to right</td>
|
|
</tr>
|
|
<tr>
|
|
<td>||</td>
|
|
<td>logical OR</td>
|
|
<td>left to right</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>?:</td>
|
|
<td>ternary</td>
|
|
<td>right to left</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>= += -=<br>
|
|
*= /= %=<br>
|
|
&= |= ^=<br>
|
|
<<= >>= >>>=
|
|
</td>
|
|
<td>assignment</td>
|
|
<td>right to left</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
|
|
<p>Note that for the calculations to work, each operand must have the correct type. For
|
|
example, if you have a local variable <strong>MyVar</strong> that has a number as a string and
|
|
you want to subtrack from it, like <code>MyVar - 1</code>, you need to convert the string in
|
|
MyVar to an integer or a float. Example: <code>int(MyVar) - 1</code> or <code>float(MyVar) - 1
|
|
</code>. The same rules apply to concatenating a string and an integer. The integer has to be
|
|
converted to a string, such as <code>"IT" + str(index)</code></p>
|
|
|
|
<p>In this example, the <strong>A1</strong> section uses the <a href="chapter3.shtml">For</a>
|
|
action to toggle 5 turnouts. The turnout user names are T1 thru T5. The
|
|
<strong><em>index</em></strong> <a href="chapter8.shtml">local variable</a> is used to supply
|
|
the number. The index value has to converted to a string before concatenating with the
|
|
"T"</p>
|
|
|
|
<p>The <strong>A2</strong> section is an example of a digital expression. This a simple one
|
|
with just the "and" operator.</p>
|
|
|
|
<div style="margin-left: 2em;">
|
|
<a href="images/chapter9/formula_example.png"><img src=
|
|
"images/chapter9/formula_example.png" alt="Chapter 9 formula_example" width="700" height=
|
|
"500"></a>
|
|
</div>
|
|
|
|
<!-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = -->
|
|
|
|
<h3 id="functions">Functions</h3>
|
|
|
|
<p>Formula supports functions, like sin(x) and random(). Some functions takes one or several
|
|
parameters. A function has an identifier, for example "sin", followed by a left parentheses,
|
|
optional one or several parameters separated by comma, and then closed by a right
|
|
parentheses.</p>
|
|
|
|
<p>The dialog boxes for editing an action or expression, and the dialog box for editing
|
|
variables, has a button "Formula functions". If you click on that button, you get a new
|
|
dialog box that shows the functions that are available and the documentation on each of
|
|
them.</p>
|
|
|
|
<p>For JMRI developers: The functions are defined in the package
|
|
<em>jmri.jmrit.logixng.util.parser.functions</em> and each module has its own Java class.
|
|
Each function is its own class that implements the <em>Function</em> interface.</p>
|
|
|
|
<!-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = -->
|
|
|
|
<h4>Function Lists</h4>
|
|
<ul>
|
|
<li>Clock
|
|
<div style="margin-left: 2em;">
|
|
<a href="images/chapter9/functions_clock.png"><img src=
|
|
"images/chapter9/functions_clock.png" alt="Chapter 9 clock functions"></a>
|
|
</div>
|
|
</li>
|
|
<li>Common
|
|
<div style="margin-left: 2em;">
|
|
<a href="images/chapter9/functions_common.png"><img src=
|
|
"images/chapter9/functions_common.png" alt="Chapter 9 common functions"></a>
|
|
</div>
|
|
</li>
|
|
<li>Convert
|
|
<div style="margin-left: 2em;">
|
|
<a href="images/chapter9/functions_convert.png"><img src=
|
|
"images/chapter9/functions_convert.png" alt="Chapter 9 convert functions"></a>
|
|
</div>
|
|
</li>
|
|
<li>Java<span class="since">since 5.1.3</span>
|
|
<div style="margin-left: 2em;">
|
|
<a href="images/chapter9/functions_java.png"><img src=
|
|
"images/chapter9/functions_java.png" alt="Chapter 9 java functions"></a>
|
|
</div>
|
|
</li>
|
|
<li>Layout<span class="since">since 4.25.8</span>
|
|
<div style="margin-left: 2em;">
|
|
<a href="images/chapter9/functions_layout.png"><img src=
|
|
"images/chapter9/functions_layout.png" alt="Chapter 9 layout functions"></a>
|
|
</div>
|
|
</li>
|
|
<li>Math
|
|
<div style="margin-left: 2em;">
|
|
<a href="images/chapter9/functions_math.png"><img src=
|
|
"images/chapter9/functions_math.png" alt="Chapter 9 math functions"></a>
|
|
</div>
|
|
</li>
|
|
<li>NamedBean
|
|
<div style="margin-left: 2em;">
|
|
<a href="images/chapter9/functions_named_bean.png"><img src=
|
|
"images/chapter9/functions_named_bean.png" alt="Chapter 9 named bean functions"></a>
|
|
</div>
|
|
</li>
|
|
<li>String
|
|
<div style="margin-left: 2em;">
|
|
<a href="images/chapter9/functions_string.png"><img src=
|
|
"images/chapter9/functions_string.png" alt="Chapter 9 string functions"></a>
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
|
|
<!-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = -->
|
|
|
|
<h4>Documentation of the functions</h4>
|
|
|
|
<p>To make it easier to use the functions, each function has some documentation.</p>
|
|
|
|
<p>Each action/expression has a <strong>Formula functions</strong> button that opens a dialog
|
|
box with documentation of the functions.</p>
|
|
|
|
<div style="margin-left: 2em;">
|
|
<a href="images/chapter9/function_1.png"><img src="images/chapter9/function_1.png" alt=
|
|
"Chapter 9 function button" width="922" height="270"></a>
|
|
</div>
|
|
|
|
<p>The functions are grouped in modules to make it easier to find the functions. Select the
|
|
module you are interested in.</p>
|
|
|
|
<div style="margin-left: 2em;">
|
|
<a href="images/chapter9/function_2.png"><img src="images/chapter9/function_2.png" alt=
|
|
"Chapter 9 select module" width="500" height="621"></a>
|
|
</div>
|
|
|
|
<p>Then select the function you are interested in.</p>
|
|
|
|
<div style="margin-left: 2em;">
|
|
<a href="images/chapter9/function_3.png"><img src="images/chapter9/function_3.png" alt=
|
|
"Chapter 9 select function" width="500" height="621"></a>
|
|
</div>
|
|
|
|
<p>In this case, the function <strong>fastClock()</strong> take a string parameter which can
|
|
have any of the values <em>hour</em>, <em>min</em> or <em>minOfDay</em>.</p>
|
|
|
|
<p>Some functions, for example the function <strong>random()</strong>, can take different
|
|
numbers of parameters. The default is <code>0.0 <= x < 1.0</code>. Supplying
|
|
<em>max</em> or <em>min and max</em> values can change the range.</p>
|
|
|
|
<div style="margin-left: 2em;">
|
|
<a href="images/chapter9/function_4.png"><img src="images/chapter9/function_4.png" alt=
|
|
"Chapter 9 random functions" width="500" height="621"></a>
|
|
</div>
|
|
|
|
<!-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = -->
|
|
|
|
<h3>Access to Java Methods</h3>
|
|
|
|
<p>Sometimes it is possible to access Java methods. For example, for most of the JMRI beans
|
|
it is possible to access the public methods in the bean's Interface. This can also apply to
|
|
some of the main Java classes, such as String.</p>
|
|
|
|
<h5>Java Arrays<span class="since">since 5.7.5</span></h5>
|
|
|
|
<p>Some Java methods return a Java Array instead of a List. The LogixNG Formula has been
|
|
enhanced to work with a Java Array.</p>
|
|
|
|
<p>Here is an example using the Java array created by the String <strong>split</strong> method.</p>
|
|
|
|
<pre style="border: 2px solid #778899; margin: 1em; padding: 0.1em;">
|
|
LogixNG: IQ:AUTO:0001
|
|
ConditionalNG: IQC:AUTO:0001
|
|
! A
|
|
Many
|
|
::: Local variable "a_b", init to String "Hello_World"
|
|
::: Local variable "a", init to None ""
|
|
::: Local variable "b", init to None ""
|
|
::: Local variable "a_b_array", init to None ""
|
|
! A1
|
|
Digital Formula: a_b_array = a_b.split("\_")
|
|
! A2
|
|
Digital Formula: a = a_b_array[0]
|
|
! A3
|
|
Digital Formula: b = a_b_array[1]
|
|
! A4
|
|
Log local variables
|
|
</pre>
|
|
|
|
<!-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = -->
|
|
|
|
<h3 id="jythonFunction">Advanced: Add new function with Jython</h3>
|
|
|
|
<p>It's possible to create a new function using Jython to be used by a formula. The code below
|
|
gives an example that you can use as a template. A new function is added by creating a new
|
|
class that extends the class <strong>Function</strong> and implements these methods:</p>
|
|
|
|
<div style="margin-left: 2em">
|
|
<table>
|
|
<tr>
|
|
<th>Method</th>
|
|
<th>Description</th>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>getModule</td>
|
|
<td>The name of the module that the function belongs to</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>getName</td>
|
|
<td>The name of the function</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>getDescription</td>
|
|
<td>Description of the function for the user</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>getConstantDescriptions</td>
|
|
<td>Description of any constants</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>calculate</td>
|
|
<td>Calculate the function</td>
|
|
</tr>
|
|
|
|
</table>
|
|
</div>
|
|
|
|
<p>Example Jython script that defines the <strong>getBlockValue</strong> function that takes
|
|
the name of a block as its parameter:</p>
|
|
|
|
<pre style="border: 2px solid #778899; margin: 1em; padding: 0.1em;">
|
|
import jmri
|
|
|
|
class BlockValue(jmri.jmrit.logixng.util.parser.Function):
|
|
def getModule(self):
|
|
return 'Custom'
|
|
|
|
def getName(self):
|
|
return 'getBlockValue'
|
|
|
|
def getDescription(self):
|
|
return 'Get the current value for the specified block name.'
|
|
|
|
def getConstantDescriptions(self):
|
|
return None
|
|
|
|
def calculate(self, symbolTable, parameterList):
|
|
if (parameterList.size() != 1):
|
|
raise jmri.jmrit.logixng.util.parser.WrongNumberOfParametersException("Function requires one parameter")
|
|
|
|
blockName = parameterList.get(0).calculate(symbolTable)
|
|
block = blocks.getBlock(blockName)
|
|
return None if block is None else block.getValue()
|
|
|
|
jmri.InstanceManager.getDefault(jmri.jmrit.logixng.util.parser.FunctionManager).put("getBlockValue", BlockValue())
|
|
</pre>
|
|
|
|
<h6>Sample LogixNG conditional</h6>
|
|
|
|
<pre style="border: 2px solid #778899; margin: 1em; padding: 0.1em;">
|
|
LogixNG: Test Script Function
|
|
ConditionalNG: Get Block Value
|
|
! A
|
|
Many
|
|
::: Local variable "BlockValue", init to None ""
|
|
! A1
|
|
Digital Formula: BlockValue = getBlockValue("TestBlock")
|
|
?* E1
|
|
|
|
! A2
|
|
Log local variables
|
|
</pre>
|
|
|
|
<!-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = -->
|
|
|
|
<h4>Calculate the function</h4>
|
|
|
|
<p>The function <strong>calculate</strong> takes a number of arguments as a
|
|
<strong>List<ExpressionNode></strong>. We first check the number of arguments by
|
|
calling the method <strong>size()</strong> and if that's correct, we get the arguments by
|
|
calling the method <strong>get(index)</strong> where "index" is the index of the
|
|
argument.</p>
|
|
|
|
<p>But to do something useful with the arguments, we need to calculate each argument we want
|
|
to use. We do that by calling the method <strong>calculate</strong> on each argument we want
|
|
to use.</p>
|
|
|
|
<p>We then do the calculation, which in this case finds the requested block and returns the
|
|
current value.</p>
|
|
|
|
<p>Since the local variables symbol table is also passed to the calculate function, local
|
|
variables are also available to the script using <code>symbolTable.getValue(name)</code> and
|
|
<code>symbolTable(name, new value)</code>.</p>
|
|
|
|
<!-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = -->
|
|
|
|
<h4>Important!</h4>
|
|
|
|
<p>The Jython script needs to loaded before the LogixNG runs that references the custom
|
|
function. The best approach is to add a start up action to run the script. When the xml
|
|
data file is loaded, the script will be ready to process the custom function request.</p>
|
|
|
|
<p>A function may set turnouts, sensors, and other things on the layout. You may for example
|
|
create the function <strong>setTurnout(turnout, newState)</strong>. But it's important to
|
|
remember that a ConditionalNG runs on a separate thread so if you set a turnout or a sensor,
|
|
you must do that on the layout thread. Formula is always run on the thread that the
|
|
ConditionalNG is run on, so if a function updates the layout or the GUI, it needs to do it on
|
|
the layout thread or the GUI thread.</p>
|
|
|
|
<p>See the later chapter on threads (may not exist yet) for more information of LogixNG
|
|
threads.</p>
|
|
|
|
<h3>Developer Note: Adding a new function</h3>
|
|
|
|
<p>Select a suitable module for the new function. Each module has its own class and most
|
|
module classes reside in the <em>jmri.jmrit.logixng.util.parser.functions</em> package,
|
|
although it's possible to put a module in another Java package if desired. If a module is
|
|
aimed for an particular part of JMRI, for example LocoNet, it might be better to put it in the
|
|
<em>jmri.jmrix.loconet.logixng</em> package.</p>
|
|
|
|
<p>The module has a <strong>getFunctions()</strong> method which tells the
|
|
<strong>FunctionManager</strong> which functions each module provides. This method returns a
|
|
set of functions. To create a new function, add a new anonymous class to this set. Have the
|
|
new class extend the <strong>AbstractFunction</strong> class.</p>
|
|
|
|
<p>The <strong>AbstractFunction</strong> constructor takes three parameters. The module, the name of the
|
|
function and the description of the function. The anonymous class then needs to implement the
|
|
<strong>calculate()</strong> method which has the actual implementation of the new function.</p>
|
|
|
|
<p>The <strong>calculate()</strong> method takes two parameters, the symbol table and the
|
|
parameters to the function. It then does the desired calculation for the function and then
|
|
returns the value. <strong>parameterList.size()</strong> gives the number of parameters for
|
|
the function. <strong>parameterList.get(index).calculate(symbolTable)</strong> returns the
|
|
value of parameter index.</p>
|
|
|
|
<p>The <em>jmri.util.TypeConversionUtil</em> class has some useful utility methods to convert
|
|
the parameters to some types, for example String, long and double.</p>
|
|
|
|
<p>Example: The <strong>sqrt()</strong> function in the Math module. The
|
|
<strong>addSqrtFunction()</strong> method is called from the MathFunctions.getFunctions()
|
|
method.</p>
|
|
|
|
<pre>
|
|
private void addSqrtFunction(Set<Function> functionClasses) {
|
|
functionClasses.add(new AbstractFunction(this, "sqrt", Bundle.getMessage("Math.sqrt_Descr")) {
|
|
@Override
|
|
public Object calculate(SymbolTable symbolTable, List<ExpressionNode> parameterList)
|
|
throws JmriException {
|
|
|
|
if (parameterList.size() == 1) {
|
|
double param = TypeConversionUtil.convertToDouble(parameterList.get(0).calculate(symbolTable), false);
|
|
return Math.sqrt(param);
|
|
} else {
|
|
throw new WrongNumberOfParametersException(Bundle.getMessage("WrongNumberOfParameters1", getName()));
|
|
}
|
|
}
|
|
});
|
|
}
|
|
</pre>
|
|
|
|
<hr>
|
|
|
|
<p><a href="chapter10.shtml">Chapter 10 - Modules</a>
|
|
</p>
|
|
|
|
<p><a href="index.shtml">Return to the Reference TOC</a>
|
|
</p>
|
|
|
|
<!--#include virtual="/help/en/parts/Footer.shtml" -->
|
|
</div>
|
|
<!-- closes #mainContent-->
|
|
</div>
|
|
<!-- closes #mBody-->
|
|
<script src="/js/help.js"></script>
|
|
</body>
|
|
</html>
|