YetAnotherAutoTrain.py -- Data driven automatic trains
Jython (Java based Python) scripting is a common tool for running trains automatically.
The Python language is relatively easy to use and provides easy access to JMRI facilities,
such as turnouts, sensors, blocks, etc. Access to JMRI throttles provides the ability to
control locomotives.
The typical script sets turnouts, acquires a locomotive, runs the locomotive while keeping
track of its location. Some scripts use occupancy sensors for the location, others rely on
time durations.
Frequently the original script needs modifications as additional details are added. When a
second train is added which has to share track with the first one, the script becomes ever
more complicated.
YAAT is designed to eliminate the programming by using text files that contain English
like phrases. For example:
sensor = sensors.getSensor('S-Sensor')
If sensor is not None:
sensor.setKnownState(ACTIVE)
vs.
Set sensor S-Sensor active
Each train has its own text file, but they can coordinate actions by using JMRI sensors,
etc.
Using YAAT
YAAT works by referencing JMRI objects, such as turnouts, sensors, etc. This means that
the layout definition needs to accurately describe the layout that will run the trains that
YAAT will be controlling.
Some of the YAAT actions have specific requirements. The signal head and signal mast
actions are obvious. The block actions need to have blocks assigned to Layout Editor track
components, such as track segments and turnouts.
Since the YetAnotherAutoTrain.py script needs to be modified, the script
should be copied from the JMRI install location to the user files location. See Help
⇒ Locations.
Global Settings
- logLevel
- YAAT sends logging information to either the JMRI system console or the script monitor
located at Scripting {Old: Panels} ⇒
Script Output. Zero is no logging, 4 provides the maximum detail.
- statusSensor
- Optional. Provide feedback to JMRI indicating that one or more train threads are
running.
- masterSensor
- Optional. If this sensor becomes active, YAAT will terminate all of the train threads.
NOTE: Trains might keep running at their last speed.
- saveYAATcompiles
-
If set to True, the train compiles will be retained. See Compiled
Trains.
Trains
Each train needs a set of actions. The orignal design had the actions included in the
script file. That feature will be removed in a future release.
Define actions:
- External File
- Create a text file with one action per line. Blank lines and lines starting with a
comment character, #, are ok. Add the train name and file name to the
trainList. The file name can be the complete path or the file name can
include a keyword for the location, such as "preference:" which is replaced by the path to
the user files location at run time.
- File of files
- In addition to modifying the script's trainList, it is possible to create a text file
that has the same format as the trainList. If the file exists, its
contents will be added to the trainList. The file is located in the
yaat directory in the user files location. The filename is
TrainList.txt. This file is optional.
- Embedded
- The actions are added to a Python list. Each action is enclosed in single or double
quotes and end with a comma at the end of the line. The embedded method requires a unique
block of code at the end of the script for each embedded action list. Note: This method
will be removed in a future release. It requires too much manual code
modification.
Action Phrases
The following descriptions used the following formatting:
- <?? | ??> — List of choices
- <????> — Required values, usually names or numbers
- Brackets, [], are optional phrases
- Text and names are case sensitive
Standard Actions
- Assign <long | short> address <dccaddr>[ as <train name>[ in
<block name>]]
- Create a JMRI throttle using the DCC addresss. If a block name is supplied, the
optional train name will be used for block tracking.
- Loop
- Marks the end of the one time start up actions, such as the throttle assignment,
positioning a train before starting repeating actions, etc.
- Print <message text>
- Display a message in the script output window or the system console log. Useful for
debugging.
- Repeat if sensor <sensor name> is <active | inactive>
- Skip the remaining steps and start over. Use the same sensor as Stop with the opposite
test. This provides a cleanup section before Stop. Requires that a "Loop" action was
included in the action list.
- Set block <block name> <occupied | unoccupied | reserved | free>
- The occupied and unoccupied states are used to simulate train movement. It works best
if a simulation sensor is used in conjunction with an If statement. Reserved and free
control the alternate track color.
- Set direction to <forward | reverse>
- Set function key <0 to 28> <on | off>[, wait <n> seconds]
- Set the function key on or off. The number can be from 0 to 28. If seconds is greater
than zero, the opposite action will be performed after the number of seconds has
passed.
- Set route <route name>
- Set sensor <sensor name> <active | inactive>
- Can be used to pass status to other trains.
- Set speed to <0 to 1.0>
- Set turnout <turnout name> <closed | thrown>[, wait <n> seconds]
- The process will wait for up to 5 seconds for turnout feedback. If seconds is entered
and greater than zero, a wait allows the turnout command to complete, capacitors to
recharge, etc.
- Start when sensor <sensor name> is <active | inactive>
- An optional action that defers running the train until the condition has been
satisfied. This can also be used to pause a train between runs.
- Stop if sensor <sensor name> is <active | inactive>
- This action needs to be the last one in the list. If the sensor state matches, the
throttle will be released and the script stopped. If it does not, the script will do the
sequence of actions again. If the Stop action is missing, the script will run forever,
until the script thread is killed, or JMRI is stopped.
- Wait for <n> seconds
- Wait until the time has expired. Normally used for station stops.
- Wait for block <block name> to become <occupied | unoccupied | reserved |
free>
- Wait for sensor <sensor name> to become <active | inactive>
- Wait for signal head <head name> to [not] show <appearance name> [or
...]
- The appearance names are language specific. Use the signal head table to get the
available appearance names.
- Wait for signal mast <mast name> to [not] display <aspect name> [or
...]
- Use the signal mast table to get the valid aspect names. Remember that the names vary
based on signal mast type.
- Wait while signal mast <mast name> speed is less than <aspect name>
speed
- Use the signal mast table to get the valid aspect names. Remember that the names vary
based on signal mast type.
If/Else/Endif Actions
The If and Endif actions are required.
The Else action is optional and is used to separate the true and
false actions. Nesting is supported.
- If block <block name> is <occupied | unoccupied | reserved | free>
- If sensor <sensor name> is <active | inactive>
- If signal head <head name> does [not] show <appearance> [or ...]
- If signal mast <mast name> does [not] display <aspect> [or ...]
- If speed for signal mast <mast name> is <eq | ne | lt | gt | le | ge>
<speed name>
- For information on speed names, look at the JMRI install location:
xml/signals/signalSpeeds.xml
- Else
- Endif
GoSub/Sub/EndSub Actions
The sub routines are placed at the end of an embedded list or text file. The sub routines
cannot be nested, but a sub routine can call another sub routine. The sub routine name cannot
have spaces. Control returns to the statement after the CallSub when the sub routine is
finished.
- CallSub <subname>
- Sub <subname>
- EndSub <subname>
Dispatcher Support (Created by Bill Fitch)
- Dispatch using file <traininfo.xml>[, type <USER, value <dccAddress> |
ROSTER, value <roster entry name> | OPERATIONS, value <train name>>]
-
The Dispatcher train info filename is required. The optional USER, ROSTER and OPERATIONS
keywords can override the train info content. Examples:
- Dispatch using file routefrom1-2.xml (uses the train info xml file set up by "Save
Train info" in "Create New Train" in dispatcher)
- Dispatch using file routefrom1-2.xml, type USER, value 3 (uses train with dcc
address 3 instead of the train in the xml file
- Dispatch using file routefrom1-2.xml, type ROSTER, value diesel104 (uses diesel104
from roster instead of the train in the xml file)
The <traininfo.xml> file will have been generated by dispatcher prior to running
YAAT and will have been placed in preference:dispatcher/traininfo by dispatcher. Note:
"preference:" is the keyword for the user files location.
YAAT Demo
A demonstration system is available at YAAT Demo. The zip file
is a JMRI profile. After expanding the zip file, place the YAAT.jmri
directory next to the other profiles and it should show up in the profile list. The demo
panel is based on JMRI 4.20.
Running the demo
After starting PanelPro, click on the Open Panel File
button and select yaat demo.xml.
Click on the Load YAAT button to start the process. After several seconds
the button label will change to Running. Click on the
Paused button to start a train. The button label will change to
Run. Cllcking again will pause the train when it finishes a loop. Click on
Stop to terimnate the train and remove it from the system.
The Stop Threads button will terminate all trains immediately. Active
trains might continue running at the last throttle setting.
Advanced Features
Compiled Trains
The standard YAAT process compiles each train from its text file or from the embedded
content. THe result of the compile process is a set of tokens that are interpreted while the
train is running. The duration of the compile step depends on the number of trains and the
number of actions.
If the saveYAATcompiles option is true, a compiled train will be used
instead of doing the compile step.
A compiled train is located at the preference:yaatp/<trainname> directory. When YAAT
is started, the date/time for each train source file is checked. If it is greater than the
compiled train, if any, the compile will occur. Otherwise the compiled version will be
loaded. Embedded trains are always compiled.
Custom Extensions
An extension is a separate Python file that contains additional actions.
Each action requires a do<name> method (def) and a
compile<name> method (def). The format of an action name is
verb_noun, such as Set_memory... The typical space for
built-in actions is replaced with an underscore.
The custom actions are added to the customExtensions dictionary. The key
is the file name and the data is a list of actions.