356 lines
15 KiB
Plaintext
356 lines
15 KiB
Plaintext
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta name="generator" content="HTML Tidy for HTML5 for Apple macOS version 5.8.0">
|
|
<meta name="keywords" content=
|
|
"start jmri jython, hello world jython, jython layout commands, jython access jmri objects, access jmri tables">
|
|
|
|
<title>JMRI: Getting Started with Scripting</title><!--#include virtual="/help/en/parts/Style.shtml" -->
|
|
</head>
|
|
<body>
|
|
<!--#include virtual="/help/en/parts/Header.shtml" -->
|
|
|
|
<div id="mBody">
|
|
<!--#include virtual="Sidebar.shtml" -->
|
|
|
|
<div id="mainContent">
|
|
<!-- Page Body -->
|
|
|
|
<h1 id="StartTopOfPage">JMRI: Getting Started with Scripting</h1>
|
|
|
|
<p>The easiest way to learn how to use scripts in JMRI is to study the simplest examples
|
|
first and work your way into more complex details. The sections below discuss various aspects
|
|
of scripting that will help you to understand the more complex scripts found in the <a href=
|
|
"Examples.shtml" target="_blank">JMRI examples library</a> included in the distribution and
|
|
online:</p>
|
|
|
|
<ul>
|
|
<li>
|
|
<a href="#helloworld">Running "Hello World"</a>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="#debugging">Basic debugging tools in JMRI</a>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="#layoutcommands">Sample layout commands</a>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="#objects">Access to JMRI Objects</a>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="#scripts">Script files, listeners, and classes</a>
|
|
</li>
|
|
</ul>
|
|
|
|
<h4>CAUTION: Python and its Jython variant do not use braces, brackets, or parentheses to
|
|
indicate program structure, but rather rely on formatting, specifically indentation. To avoid
|
|
confusion, it is STRONGLY recommended that you DO NOT USE TABS but rather use spaces (four is
|
|
typical) to show statements that part of a class or controlled structure where you might have
|
|
used some bracketing. Most program editors have a setting to automatically changes tabs to a
|
|
set number of spaces to make this easier.</h4>
|
|
|
|
<h2 id="helloworld">Running "Hello World"</h2>
|
|
|
|
<div class="para">
|
|
There are several ways to use Python scripts with JMRI. The easiest is to use the built-in
|
|
support in the standard JMRI applications: PanelPro, DecoderPro, etc.<br>
|
|
To do that:
|
|
<ul>
|
|
<li>
|
|
<a href="../../../package/jmri/jmrit/beantable/images/jmriScriptWindow.png"><img src=
|
|
"../../../package/jmri/jmrit/beantable/images/jmriScriptWindow.png" width="250" height=
|
|
"136" class="floatRight" alt="JMRI Jython Script Window"></a>From the
|
|
<strong>Scripting</strong> {<em>Old: <strong>Panels</strong></em>} menu, select
|
|
<strong>Script Entry</strong>. This will give you a window where you can type one or
|
|
more commands to execute. (Note that it might take a little while the first time you do
|
|
this as the program finds its libraries; it will be faster the next time) The commands
|
|
stay in the window, so you can edit them and rerun them until you like them.
|
|
</li>
|
|
|
|
<li>From the <strong>Scripting</strong> {<em>Old: <strong>Panels</strong></em>} menu,
|
|
select <strong>Script Output</strong>. This creates a window that shows the output from
|
|
the script commands you issue.</li>
|
|
|
|
<li>To see this in operation, type
|
|
|
|
<pre style="font-family: monospace;">
|
|
print "Hello World"
|
|
|
|
</pre>in the input window, and click "Execute". You should see the output in the Script Output
|
|
window immediately:
|
|
|
|
<pre style="font-family: monospace;">
|
|
>>> print "Hello World"
|
|
Hello World
|
|
|
|
</pre>
|
|
</li>
|
|
|
|
<li>Python also evaluates expressions, etc. Remove the contents of the input window
|
|
(select it and hit delete), and enter
|
|
|
|
<pre style="font-family: monospace;">
|
|
print 1+2
|
|
|
|
</pre>then click execute. The output window should then show something like:
|
|
|
|
<pre style="font-family: monospace;">
|
|
>>> print 1+2
|
|
3
|
|
|
|
</pre>
|
|
</li>
|
|
</ul>
|
|
|
|
<p><a href="#StartTopOfPage">[Go to top of page]</a>
|
|
</p>
|
|
</div>
|
|
|
|
<h2 id="debugging">Basic debugging tools in JMRI</h2>
|
|
<!--Section added by Jerry Grochow 2018-10-18 based on comments found in the JMRI Users online forum from
|
|
Cliff in BajaSoCal and Bob Jacobson-->
|
|
|
|
<p>JMRI does not provide a full "development environment," but it does provide some basic
|
|
information that can help with debugging your scripts.</p>
|
|
|
|
<p>Open the JMRI System Console window (<strong>Help ⇒ System Console</strong>). This will
|
|
give you instant information pertaining to WARN and ERROR messages. Text can be copied to a
|
|
text editor for further review.</p>
|
|
|
|
<p>Some Jython errors create a Java exception which will be displayed in the System Console.
|
|
If the Java error does not include the script line number, finding the source of the problem
|
|
can be difficult.</p>
|
|
|
|
<p>The Java exception will normally contain a line that points to the current Python
|
|
<strong>def</strong> or the implied <strong>def</strong> for the first level code. Look for the
|
|
<strong>at</strong> line that contains <strong><script>:</strong> followed by a number.
|
|
The number is the line number of the last line in the current <strong>def</strong>. This helps
|
|
narrow the scope of code to be reviewed.</p>
|
|
|
|
<pre>
|
|
12:41:20,129 jmri.jmrit.automat.AbstractAutomaton WARN - Unexpected Exception ends AbstractAutomaton thread [theInstan20]
|
|
org.python.core.PyException: TypeError: cannot concatenate 'str' and 'int' objects
|
|
at org.python.core.Py.TypeError(Py.java:236) ~[jython-standalone-2.7.2.jar:2.7.2]
|
|
at org.python.core.PyObject._basic_add(PyObject.java:2091) ~[jython-standalone-2.7.2.jar:2.7.2]
|
|
at org.python.core.PyObject._add(PyObject.java:2068) ~[jython-standalone-2.7.2.jar:2.7.2]
|
|
at org.python.pycode._pyx11.handle$4(<strong><script>:993</strong>) ~[?:?]
|
|
at org.python.pycode._pyx11.call_function(<script>) ~[?:?]
|
|
at org.python.core.PyTableCode.call(PyTableCode.java:173) ~[jython-standalone-2.7.2.jar:2.7.2]
|
|
...
|
|
</pre>
|
|
|
|
|
|
<p>Open the Script Output window (<strong>Scripting</strong> {<em>Old:
|
|
<strong>Panels</strong></em>} <strong>⇒ Script Output</strong>). This will display the
|
|
results of any "print" statements in your script as well as some error messages (NOTE that
|
|
some errors go here and some to the System Console so you you should have both windows open
|
|
when you are debugging). Text can be copied to a text editor for further review.</p>
|
|
|
|
<p>Investigate the information found in the <a href="../../doc/Technical/Logging.shtml"
|
|
target="_blank">technical reference</a> to be better able to insert more of your own DEBUG
|
|
and INFO messages into the session.log file, and also the JMRI System Console window. You
|
|
might also investigate the "default_lcf.xml" file that is described in that link.</p>
|
|
|
|
<p>Note to Mac OS users: BBEdit and similar tools do some syntax checking. For PC users,
|
|
Notepad++ is a good tool for getting indentation correct.</p>
|
|
|
|
<p><a href="#StartTopOfPage">[Go to top of page]</a></p>
|
|
|
|
<h2><a id="layoutcommands">Sample layout commands</a>
|
|
</h2>
|
|
|
|
<div class="para">
|
|
<pre style="font-family: monospace;">
|
|
|
|
>>> lt1 = turnouts.provideTurnout("1")
|
|
|
|
>>> lt1.setCommandedState(CLOSED)
|
|
|
|
>>> print lt1.commandedState
|
|
2
|
|
|
|
>>> lt1.commandedState = THROWN
|
|
|
|
>>> print lt1.commandedState
|
|
4
|
|
|
|
>>> turnouts.provideTurnout("1").getCommandedState()
|
|
1
|
|
</pre>
|
|
<p>Note that this is running a complete version of the JMRI application; all of the windows
|
|
and menus are presented the same way, configuration is done by the preferences panel, etc.
|
|
What the Jython connection adds is a command line from which you can directly manipulate
|
|
things.</p>
|
|
|
|
<p>This also shows some of the simplifications that Jython and the Python language brings
|
|
to using JMRI code. The Java member function:</p>
|
|
|
|
<pre style="font-family: monospace;">
|
|
turnout.SetCommandedState(jmri.Turnout.CLOSED);
|
|
</pre>can also be expressed in Jython as:
|
|
|
|
<pre style="font-family: monospace;">
|
|
turnout.commandedState = CLOSED
|
|
</pre>
|
|
<p>This results in much easier-to-read code.</p>
|
|
|
|
<p>There are a lot of useful Python books and online tutorials. For more information on the
|
|
Jython language and it's relations with Java, the best reference is the <a href=
|
|
"https://www.oreilly.com/library/view/jython-essentials/9781449397364/" target="_blank">Jython Essentials</a> book
|
|
published by O'Reilly. The <a href="https://www.jython.org/" target="_blank">jython.org web
|
|
site</a> is also very useful.</p>
|
|
|
|
<p><a href="#StartTopOfPage">[Go to top of page]</a>
|
|
</p>
|
|
</div>
|
|
|
|
<h2 id="objects">Access to JMRI Objects</h2>
|
|
|
|
<div class="para">
|
|
JMRI uses the factory-pattern extensively to get access to objects. In Java this results in
|
|
verbose code like
|
|
|
|
<pre style="font-family: monospace;">
|
|
Turnout t2 = InstanceManager.getDefault(TurnoutManager.class).newTurnout("LT2", "turnout 2");
|
|
t2.SetCommandedState(Turnout.THROWN)
|
|
</pre>Jython simplifies that by allowing us to provide useful variables, and by shortening certain
|
|
method calls.
|
|
<p>To get easier access to the SignalHead, Sensor and Turnout managers and the
|
|
CommandStation object, several shortcut variables are defined:</p>
|
|
|
|
<ul>
|
|
<li>sensors</li>
|
|
|
|
<li>turnouts</li>
|
|
|
|
<li>lights</li>
|
|
|
|
<li>signals (SignalHeads)</li>
|
|
|
|
<li>masts (SignalMasts)</li>
|
|
|
|
<li>routes</li>
|
|
|
|
<li>blocks</li>
|
|
|
|
<li>reporters</li>
|
|
|
|
<li>idtags</li>
|
|
|
|
<li>memories</li>
|
|
|
|
<li>powermanager</li>
|
|
|
|
<li>addressedProgrammers</li>
|
|
|
|
<li>globalProgrammers</li>
|
|
|
|
<li>dcc (current command station)</li>
|
|
|
|
<li>audio</li>
|
|
|
|
<li>shutdown (current shutdown manager)</li>
|
|
|
|
<li>layoutblocks</li>
|
|
|
|
<li>warrants</li>
|
|
|
|
<li>sections</li>
|
|
|
|
<li>transits</li>
|
|
</ul>
|
|
These can then be referenced directly in Jython as
|
|
|
|
<pre style="font-family: monospace;">t2 = turnouts.provideTurnout("12");
|
|
</pre>Note that the variable t2 did not need to be declared.
|
|
<p>Jython provides a shortcut for parameters that have been defined with Java-Bean-like get
|
|
and set methods:</p>
|
|
|
|
<pre style="font-family: monospace;">
|
|
t2.SetCommandedState(Turnout.THROWN)
|
|
</pre>can be written as
|
|
|
|
<pre style="font-family: monospace;">
|
|
t2.commandedState = THROWN
|
|
</pre>
|
|
<p>where the assignment is actually invoking the set method.</p>
|
|
|
|
<p>Also note that THROWN was defined when running the Python script at startup;<br>
|
|
CLOSED, ACTIVE, INACTIVE, ON, OFF, INCONSISTENT, <br>
|
|
LOCKED, UNLOCKED, PUSHBUTTONLOCKOUT, CABLOCKOUT, <br>
|
|
RED, YELLOW, GREEN, LUNAR, DARK, <br>
|
|
FLASHRED, FLASHYELLOW, FLASHGREEN are also defined. ( <a href=
|
|
"https://github.com/JMRI/JMRI/blob/master/java/src/jmri/script/JmriScriptEngineManager.java">
|
|
These shortcut bindings are defined in Java</a> )</p>
|
|
|
|
<p>A similar mechanism can be used to check the state of something:</p>
|
|
|
|
<pre style="font-family: monospace;">
|
|
>>> print sensors.provideSensor("3").knownState == ACTIVE
|
|
1
|
|
>>> print sensors.provideSensor("3").knownState == INACTIVE
|
|
0
|
|
</pre>Note that Jython uses "1" to indicate true, and "0" to indicate false, so sensor 3 is
|
|
currently active in this example. You can also use the symbols "True" and "False" respectively.
|
|
<p>You can directly invoke more complicated methods, e.g. to send a DCC packet to the rails
|
|
you type:</p>
|
|
|
|
<pre style="font-family: monospace;">
|
|
|
|
dcc.sendPacket([0x01, 0x03, 0xbb], 4)
|
|
</pre>This sends that three-byte packet four times, and then returns to the command line. Also see
|
|
<a href="WhatWhere.shtml#names">JMRI Jython : The difference between System Names and User
|
|
Names</a>
|
|
<p><a href="#StartTopOfPage">[Go to top of page]</a>
|
|
</p>
|
|
</div>
|
|
|
|
<h2 id="scripts">Script files, listeners, and classes</h2>
|
|
|
|
<div class="para">
|
|
<p>[See also <a href="HowTo.shtml" target="_blank">the scripting "How To" page</a> for more
|
|
information on these topics.]</p>
|
|
|
|
<p>Scripting would not be very interesting if you had to type the commands each time. So
|
|
you can put scripts in a text file and execute them by selecting the "Run Script..." menu
|
|
item, or by using the "Advanced Preferences" to run the script file when the program
|
|
starts.</p>
|
|
|
|
<p>Although the statements we showed above were so fast you couldn't see it, the rest of
|
|
the program was waiting while you run these samples. This is not a problem for a couple
|
|
statements, or for a script file that just does a bunch of things (perhaps setting a couple
|
|
turnouts, etc) and quits. But you might want things to happen over a longer period, or
|
|
perhaps even wait until something happens on the layout before some part of your script
|
|
runs. For example, you might want to reverse a locomotive when some sensor indicates it's
|
|
reached the end of the track.</p>
|
|
|
|
<p>There are a couple of ways to do this. First, your script could define a "listener", and
|
|
attach it to a particular sensor, turnout, etc. A listener is a small subroutine which gets
|
|
called when whatever it's attached to has it's state change. For example, a listener
|
|
subroutine attached to a particular turnout gets called when the turnout goes from thrown
|
|
to closed, or from closed to thrown. The subroutine can then look around, decide what to
|
|
do, and execute the necessary commands. When the subroutine returns, the rest of the
|
|
program then continues until the listened-to object has it's state change again, when the
|
|
process repeats.</p>
|
|
|
|
<p>For more complicated things, where you really want your script code to be free-running
|
|
within the program, you define a "class" that does what you want. In short form, this gives
|
|
you a way to have independent code to run inside the program. But don't worry about this
|
|
until you've got some more experience with scripting.</p>
|
|
|
|
<p><a href="#StartTopOfPage">[Go to top of page]</a>
|
|
</p>
|
|
</div>
|
|
<!--#include virtual="/help/en/parts/Footer.shtml" -->
|
|
</div>
|
|
<!-- closes #mainContent-->
|
|
</div>
|
|
<!-- closes #mBody-->
|
|
<script src="/js/help.js"></script>
|
|
</body>
|
|
</html>
|