Support: The JMRI LocoNet® implementation

This page describes various high-level structures of the JMRI LocoNet® implementation. Please also read the Javadocs for the jmrix.loconet package.

LocoNet-specific values

The LnConstants class provides static, final constants to represent various fields and values in LocoNet messages. At some point, some of this should be built into to the specific classes (i.e. LocoNetMessage) so the coding and decoding algorithms don't have to appear in so many places.

Sending and receiving LocoNet messages

The LocoNetInterface class provides the basic connection to a LocoNet for user classes. Messages are sent by passing them to a LocoNetInterface implementation, and you can register with a LocoNetInterface to be notified of all LocoNet traffic.

UML of JMRI's LocoNet Interface

The LocoNetMessage class represents the basic message. Currently (since July 2001), this class doesn't really help other code construct and decode LocoNet packets, but rather just contains them. This should be improved.

The steps to send a message to the LocoNet are:

  1. Create a LocoNetMessage object, and fill it with the message you want to send. It's not necessary to fill in the error-check byte; that will be done as part of sending.
  2. Locate an object providing a LocoNetInterface interface. In many cases, the LnTrafficController is providing this, and the object can be located with
            LocoNetInterface l = LnTrafficController.instance();
              

    Since JMRI 4.11.6 The LnTrafficController is connected to a given LocoNetSystemConnectionMemo, and the object can be located with
            LocoNetInterface l = memo.getLnTrafficController();
              
  3. Send the message:
            l.sendLocoNetMessage(msg);
            
    

Classes that want to receive inbound LocoNet packets should implement the LocoNetListener interface, and register their desire to listen via an object with a LocoNetInterface interface. It's important to note that listener objects can't assume that they receive incoming LocoNet messages in any specific thread. In particular, they should not assume that they receive these messages in a GUI thread, so they'll have to forward any changes to the user interface.

Implementing the LocoNet connection

Implementing communication with a real LocoNet is handled by classes that implement the LocoNetListener interface. There are currently three (see below): LnTrafficController and its subclasses LnPacketizer and LnTrafficRouter.

LnTrafficController

The LnTrafficController abstract class provides some common implementation for it's subclasses, and adds a mechanism to find a usable LocoNetInterface implementation.

The routine addLocoNetListener and removeLocoNetListener methods are implemented here, along with a notify method to forward LocoNetMessages to the listeners.

Until JMRI version 4.11.5 the static instance() method was used by a large number of jmrix.loconet classes to find a LocoNetInterface for transmitting and receiving messages. It worked through a "self" static member, which was initialized when a LnTrafficController subclass object was created. All objects wanting to send or receive over the LocoNet will thence use the last-created LnTrafficController implementation.
See the section on "Startup" for more information on this.

UML of JMRI's LocoNet Traffic Controller

LnPacketizer

The LnPacketizer class extends the LnTrafficController implementation to send and receive packets over a LocoBuffer serial link to a LocoNet. It works with an implementation of the LnPortController - Abstract class, which works at the level of character streams. These communicate through Java streams which carry the LocoNet messages as character sequences. LnPortController implementations are available for the LocoBuffer, MS100 and for reading from a hex log file.

It uses separate threads for transmission and reception of characters from the streams. The receive operation is done in a thread so it can easily stall when no messages are available. The transmit operation is done in a thread for a similar reason; sometimes a LocoBuffer will shut off input (output from the program), which causes the stream write operations to stall. By doing those in a separate thread, we can detect or at least bypass this without the entire program coming to a stop.

LnTrafficRouter

The LnTrafficRouter class provides a scatter-gather operation for the LocoNetListener interface. Note that this implementation doesn't transform the LocoNetMessages into serial traffic.
Drawing of how various objects route messages
Note the LnTrafficRouter object. It provides a LocoNetInterface for all the LocoNet-using messages in the remote node, so that only one copy of each message has to travel across the remote link.

Note that the "some remote comm class" could also be implemented as a subclass of LnTrafficRouter, instead of communicating with one.

Startup

There are "action" classes that connect to an input source. The main program puts these in a menu, on a button, etc, so that the user can select the connection desired.
The current (March 2002) set is:

In addition to configuring the adapter for the input source, something has to configure the complete set of Manager objects and the LocoNet-handling objects. These include: These last three provide LocoNet-based services for the higher-level JMRI interfaces.

The configure() methods in the various adapter classes do this. That's not a very general mechanism. Although a LnPacketizer is the right thing to connect to each of the serial port adapters, the rest of the configuration might vary.

Port adapters

LnPortController is an abstract base class to carry common implementations for the Adapter classes that connect to serial ports with specific protocols.

MS100

Note that the current MS100 implementation is not as robust as we really need it to be. In particular, back-off and retransmit is not being checked. Other interface devices, such as the Digitrax PR3, the LocoBuffer, LocoBuffer-II, and LocoBuffer-USB all implement an internal microcontroller which handles back-off and retransmit operations properly and are therefore preferred over the MS100.

The MS100Action class (package jmrix.loconet.ms100) starts up a LocoNet connection via a MS100. When triggered, it creates a visible MS100Frame object.

In turn, the MS100Frame creates an MS100Adapter object, then shows the available comm ports, allowing the user to pick one. The MS100Adapter object implements the LnPortController interface, so it can eventually connect an LnTrafficController to a serial port and MS100.

When the "Open MS100 port" button is pressed, the MS100Frame object

LocoBuffer

Very similar to the MS100 case, with the same sequence of operations. The port setup is somewhat different. Classes are in the jmrix.loconet.locobuffer package.

HexFile

The HexFile classes (package jmrix.loconet.hexfile) are meant to simulate a LocoNet connection from a data file. They provide the "LocoNet Simulator" connection type. A hexadecimal format data file feeds in messages as if they came from an outside connection.

Initialization is provided by the HexFileAction class. When triggered, it creates a visible HexFileFrame object. This provides a button the user can use to select an input file.

When a file is selected, the HexFileFrame object

Unlike the MS100Frame and LocoBufferFrame objects, the HexFileFrame object stays visible so that it can control the flow of messages from the file.

Slot and Programmer management

"Slots" are basic to the operation of a LocoNet command station. They are represented by the LocoNetSlot class. Like LocoNetMessage, this class does not (yet) provide a lot of support for creating and decoding slot status. The SlotManager class listens to LocoNet traffic to keep an up-to-date idea of the command stations slot contents. It ma someday be necessary for the SlotManager to actively communicate with the command station to update that information, but for not the SlotManager only listens to slot change commands that originate on the LocoNet or are transmitted from the program.

The SlotListener interface should be implemented by any class that wants to be notified when a slot changes.

Because Digitrax command stations handle programming via a special reserved slot, the jmri.Programmer interface is also implemented by the loconet.SlotManager class. This greatly complicates the class, but is acceptable for now.

LocoNet® is a registered trademark of Digitrax, Inc.