This page describes various high-level structures of the JMRI LocoNet® implementation. Please also read the Javadocs for the jmrix.loconet package.
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.
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:
LocoNetInterface l = LnTrafficController.instance();
LocoNetInterface l = memo.getLnTrafficController();
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 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.
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.
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.
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.
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:
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.
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
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.
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.
"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.