JMRI Support - LocoNet® Implementation
Author: Bob Jacobsen
Keywords: JMRI LocoNet technical implementation

# 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.

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 would thence use the last-created LnTrafficController implementation.
See the section on "Startup" for more information on this.

### 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.
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

Until JMRI 4.11.5 "action" classes connected to an input source. In the current code Since JMRI 4.11.6 "ConnectionConfig" classes connect an adapter to an input source. The main program reads them from the LnConnectionTypeList to create a combo box in the Connection Config pane, so that the user can select the connection desired.
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:

- Typically a LnPacketizer, which becomes the "instance" for objects that use memo.getLnTrafficController() to locate an interface.
- A Programmer instance
- A PowerManager instance
- TurnoutManager etc. instances

These last three provide LocoNet-based services for the higher-level JMRI interfaces (the `instance()` method was deprecated to support multiple connections. The `configure()` methods in the various adapter classes now handle this.)

Note this wasn't a 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 and PR4, Digitrax DCS240 and DCS52 USB interfaces, and the LocoBuffer, LocoBuffer-II, and LocoBuffer-USB devices, all implement an internal microcontroller which handles back-off and retransmit operations properly and are therefore preferred over the MS100.

The ConnectionConfig class (in 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

- passes the selected communication port to the MS100Adapter. The MS100 adapter then creates a new LocoNetSystemConnectionMemo() object, connects to that port and creates input and output streams
- Since JMRI 4.11.6 then makes sure that a LnTrafficController object exists by calling the memo.getLnTrafficController() method
  (providing multi-connection support and replacing the use of LnTrafficController.instance() used before)
- connects that LnTrafficController instance to the MS100Adapter (subclass of LnPortController)
- starts LnTrafficController in a new thread so that it can handle inbound messages asynchronously.

#### 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

- creates a HexFileAdapter object connected to that file
- The HexFileAdapter adapter then creates a new LocoNetSystemConnectionMemo() object, connects to that port and creates input and output streams
- Since JMRI 4.11.6 then makes sure that a LnTrafficController object exists by calling the memo.getLnTrafficController() method
  (providing multi-connection support and replacing the use of LnTrafficController.instance() used before)
- connects that LnTrafficController instance to the HexFileAdapter (subclass of LnPortController)
- starts LnTrafficController in a new thread so that it can handle inbound messages asynchronously.

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

#### LocoBuffer II

#### LocoBuffer USB

#### Uhlenbrock Intellibox I

#### LocoNet over TCP

#### LocoNet RMI Server

#### LocoNet PR2/3/4

#### Uhlenbrock Intellibox-II (USB)

#### DCS52 (USB)

#### DCS240 (USB)

## 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.