JMRI: Layout Automation in Java
The JMRI library provides a very powerful set of tools for automating your layout. If the tools built into PanelPro or the scripting capability aren't sufficient to do what you want, writing your own control program certainly will be.
This page describes the JMRI Java classes that support writing "automation" code in Java. That's things like operating signals, running trains, and even subtle things like changing decoder sound volumes when a train goes into a tunnel.
Note that you can also write your automation code using Jython as JMRI "scripts". For many purposes, this can be simpler because it's more interactive and has better debugging support. Java is really best for large and/or complex cases, or if you're writing something that you want to eventually contribute to JMRI itself.
The "Automat" class provides an easy way to write Java automation for your layout using JMRI.
The key class is AbstractAutomaton, which provides threading support to simplify custom automation code.
Key aspect: These run in a separate thread, so they can wait for something to happen while the rest of the program proceeds. Effectively, each Automat is a separate program running when it needs to.
There are several examples:
- SampleAutomaton - watches a Sensor, and adjusts the state of a Turnout so that it matches the Sensor's state.
- SampleAutomaton2 - watches a Sensor, and adjusts the momentum of a locomotive decoder using ops-mode programming when the sensor state changes.
- SampleAutomaton3 - runs a locomotive back and forth on a piece of track by watching two sensors, reversing the locomotive when they change state.
Note that they may not do anything useful on your railroad, as they have block and locomotive numbers hard-coded. They're meant to be examples for your own programming, not useful tools.
- Controlling Locomotives
// get the throttle instance for short address 3
throttle = getThrottle(3, false);
This code snippet assigns "short address 3" to the "throttle" variable (the "false" selects short address; long address would have been "true"). If this doesn't work for some reason, you'll get a periodic "still waiting for throttle" message in the Java window, and the program will wait.
- Interacting with the user
You can put up some message boxes directly from Java, but it takes a little code to do it. To simplify it at the start, I've added a "MsgFrame" helper class to the JMRI library.
To make a message box, you first make an object of the "MsgFrame" class:
MsgFrame box = new MsgFrame();
(You can call it whatever you'd like, of course). That line should go with the other declarations, e.g. right after the "DccThrottle throttle = null;" line.
Then, inside the test routines, there are a few things you can do. To show a message and wait for the user to click a "Continue" button, do:
box.show("my message text goes here", true);
The 2nd argument shows the "Continue button and waits if true, and just continues if false. So if you'd like to show a status message while the program continues to run, do:
box.show("status message text", false);
If you've done that, you can change the message with another "show", or you can make the box go away with:
Java actually has to do some work to pop the box and draw it. We don't want that to delay the main test program, so I've written this helper class to do all that kind of work as a 2nd, lower priority. You should be able to put messages up on the screen without significantly slowing operation down (unless the program waits for the "Continue" button to be pressed, of course). And waiting for "Continue" to be pressed won't stop the operation of the rest of the program.
- Running Your Code
There are two parts to doing this: Making your Java code
available to JMRI, and invoking it.
The easiest way to make your code accessible is to make a .jar file and place that in the "lib" directory in the JMRI program directory. Alternately, you can create a "classes" directory, and compile your Java class into that. In that case, it should end up in:
classes/MyCode.classif you've specified no code in your package, or
classes/jmri/jmrit/automat/SampleAutomaton.classas an example of how code in a specific package gets stored.
The easiest way to invoke your code is to use JMRI's scripting support. Open a Script Output window (so you can see errors) from the Panels menu, then a Script Entry window, enter:
import jmri.jmrit.automat jmri.jmrit.automat.SampleAutomaton().start()and hit Execute. The 2nd line creates an instance of your class and tells it to start running.
Once you've got that working, you can put that in a little script file and invoke it at startup, via a Logix, or whenever you desire.
Back to the Automation Help page.