JMRI: Scripting FAQ
Frequently asked questions about scripting JMRI with Jython
Where can I learn more about the Jython language?
See the Python & JMRI page for more info, including pointers to articles, etc. See also the navigation links to the left.
How do Jython and Python differ?
For the purposes of writing JMRI scripts, they don't differ very much. Most of the differences involve what happens in somewhat contrived error cases. There are also some restrictions on what you can do with the computer's configuration information, etc, in Jython, but these are not things a JMRI script is likely to need.
Some additional information on the differences is available here.
Where can I find some examples of JMRI scripts?
What do those words like
"import",
"class", etc, in the example files mean?
The imports allow you to refer to things by shorter names, essentially telling jython "search the jarray, jmri packages and recognize all the names there". For somebody trying to understand this script, you can just treat them as "ensuring the program can find parts we want".
"class" means "start the definition of a group of things that go together" (all you other experts, please don't jump on me about this; I understand both intrinsic/extrinsic polymorphism, I'm just trying to get the general idea across).
For example, in the SigletExample.py file is a description of a "class" called SigletExample, which contains two routines/functions/members: A subroutine called "defineIO", and one called "setOutput"
This "class" is associated with another called "Siglet" (actually jmri.jmrit.automat.Siglet; that's that long naming thing again), which knows when to invoke routines by those two names to get done what you want.
Essentially, you're defining two parts ("defineIO" & "setOutput") that plug into a pre-existing structure to drive signals. That pre-existing structure is very powerful, and lets you do all sorts of things, but also provides this method to try to keep it simpler.
OK, at this point most people's eyes are fully glazed over. Your best bet when starting with this stuff is to use the "copy and modify" approach to software development. It's good to try to understand the entire content of the file, but don't worry about understanding it well enough to be able to recreate it from scratch. Instead, just modify little bits and play with it.
Are there required naming conventions?
For example, "self.to12" is just the name of a variable. You can call it anything you want, e.g. self.MyBigFatNameForTheLeftTurnout
The "self" part makes it completely local; "self" refers to "an object of the particular class I'm defining right now". Alternately, you can define a global variable, but that's not recommended. If you have multiple scripts running (and you can have as many as you'd like; we recommend that you put each signal head in a separate one), the variables can get confused if you use the same variable name to mean too different things. Using "self" like this one does makes sure that doesn't happen.
Note that turnouts, etc, do have "System Names" that look like "LT12". You'll see those occasionally, but that's something different from the variable names in a script file.
What's the difference between the "Siglet" and "AbstractAutomaton" classes?
Some examples use the AbstractAutomaton class as a base, while others use the Siglet class. This is because those are intended for two different purposes.
"Siglet" is meant to be used for driving signals. You provide two pieces of code:
The Siglet base class then handles all of the listening for changes, setting up for parallel execution, etc. Your defineIO routine will be called once at the beginning, and after than any time one or more of the inputs changes, your setOutput routine will be called to recalculate the signal appearance.
Of course, you can use this class to calculate other things than signal appearances. But the key element is that the calculation is redone when the inputs change, and only when the inputs change.
AbstractAutomaton is a more general class that's intended to allow more powerful operations (and Siglet actually uses that more powerful base). You define two functions:
- init
- which is called exactly once to do any one-time setup you need
- handle
- which is called over and over and over again until it returns FALSE.
For more info on the routines that AbstractAutomaton provides to help you, see the JavaDocs for the class. (Scroll down to the section called "Method Summary")
How can I limit the priority of a script?
If the script runs a loop that's supposed to update something, it can't be written to run continuously or else it will just suck up as much computer time as it can. Rather, it should wait.
The best thing to do is to wait for something to change. For example, if your script looks at some sensors to decide what to do, wait for one of those sensors to change (see the sample scripts for examples)
Simpler, but not as efficient, is to just wait for a little while before checking again. For example
waitMsec(1000)
causes an automaton or Siglet script to wait for
1000 milliseconds (one second) before continuing.
For just a simple script, something that doesn't use the Automat or Siglet classes, you can sleep by doing
from time import sleep sleep(10)The first line defines the "sleep" routine, and only needs to be done once. The second line then sleeps for 10 seconds. Note that the accuracy of this method is not as good as using one of the special classes.