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.

Note: The JMRI preference: keyword refers to the user files location. See Help ⇒ File Locations to see the current path for the user files location.

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 ⇒ File Locations.

YAAT 3.0 — YetAnotherAutoTrain3.py

The process for managing train definitions has been changed so that the script does not need to be manually changed. The script can be run from the standard JMRI install location.

The script options are stored in the preference:yaat/config.txt file. If the file does not exist, a file will be created with a default set of options.

The train definition text files are also stored in the preference:/yaat directory.

When the script is started, the train text file names listed in the preference:/yaat/LoadTrains.txt file will be loaded. Each line in the file is a single file name, such as Back and Forth.txt. Train file names not listed in the LoadTrains.txt can be loaded on demand using the memory variable specified by the yaatMemory option.

The support for embedded train definitions has been removed.

The support for custom extensions has been removed.

The detail comments in the script have been removed. The script was approaching the JMRI limit on script file size. The content was previously moved to this help page.

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.
yaatMemorysince YAAT 3.0
Optional memory variable that contains a filename for starting a train. This makes it possible to start a train using Logix, LogixNG, scripting or even a YAAT train using the Set <memory name> to <value> YAAT action.

Trains

The following applies to YAAT 2.3 and below. See above for YAAT 3 changes.

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:

Standard Actions

Assign <long | short> address <dccaddr>[ as <train name>[ in <block name>]]
Create a JMRI throttle using the DCC address. If a block name is supplied, the optional train name will be used for block tracking.
Haltsince YAAT 3.0
Do an unconditional stop of the train. Prior to doing the Halt action, the throttle speed should be set to zero and the throttle should be released. This is generally used when starting another train using the Set memory action.
Hold signal head <name>since YAAT 3.2
Enable the Held state for the signal head table entry.
Hold signal mast <name>since YAAT 3.2
Enable the Held state for the signal mast table entry.
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.
Release signal head <name>since YAAT 3.2
Disable the Held state for the signal head table entry.
Release signal mast <name>since YAAT 3.2
Disable the Held state for the signal mast table entry.
Release throttlesince YAAT 3.0
Normally the throttle is released by the Stop action. This action can be used when preparing to chain train definitions.
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 68> <on | off>[, wait <n> seconds]
Set the function key on or off. The number can be from 0 to 68. If seconds is greater than zero, the opposite action will be performed after the number of seconds has passed.
Set memory <memory name> to <value>since YAAT 3.0
Set the memory value to a constant. By setting the memory variable to a YAAT train definition, it is possible to chain YAAT trains.
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.
Set turntable <turntable name> on panel <panel name> to ray <#> since YAAT 2.3
Set the turntable position to the ray index using the setPosition(int) method. The turntable name is the internal name on the layout editor panel, such as TUR1. The panel name is the panel's title. The time required to move the turntable bridge is variable. The following statement should be a wait for n seconds or a wait for sensor active if a turntable position sensor is available for each ray.
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> [to <n>] seconds
Wait until the time has expired. Normally used for station stops.
The optional second number is used to provide a random wait time between the two values.since YAAT 3.1
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.

A second demonstration system which includes an example for turntable control and linking trains is available at YAAT3 Demo. The zip file is a JMRI profile. After expanding the zip file, place the YAAT3.jmri directory next to the other profiles and it should show up in the profile list. The demo panel is based on JMRI 5.3.7.

Running the demo

After starting PanelPro, click on the Open Panel File button and select yaat demo.xml.

YAAT demo

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. Clicking again will pause the train when it finishes a loop. Click on Stop to terminate 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. 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 in the preference:yaatp/<trainname> directory. When YAAT is started, the date/time for each train source file is checked when it is loaded. If it is greater than the compiled train, if any, the compile will occur. Otherwise the compiled version will be loaded.

Custom Extensions

This feature has been removed from YAAT 3.0.

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.