Files
JIMRI/help/en/html/hardware/loconet/LocoNetClasses.shtml
T
2026-06-17 14:00:51 +02:00

312 lines
15 KiB
Plaintext

<!DOCTYPE html>
<html lang="en">
<head>
<meta name="generator" content="HTML Tidy for HTML5 for Apple macOS version 5.8.0">
<title>JMRI Support - LocoNet&reg; Implementation</title>
<meta name="author" content="Bob Jacobsen">
<meta name="keywords" content="JMRI LocoNet technical implementation">
<!--#include virtual="/help/en/parts/Style.shtml" -->
</head>
<body>
<!--#include virtual="/help/en/parts/Header.shtml" -->
<div id="mBody">
<!--#include virtual="Sidebar.shtml" -->
<div id="mainContent">
<h1>Support: The JMRI LocoNet&reg; implementation</h1>
<p>This page describes various high-level structures of the JMRI LocoNet&reg; implementation.
Please also read the <a href=
"https://www.jmri.org/JavaDoc/doc/jmri/jmrix/loconet/package-summary.html">Javadocs for the
jmrix.loconet package</a>.</p>
<h2>LocoNet-specific values</h2>
<p>The <a href="https://www.jmri.org/JavaDoc/doc/jmri/jmrix/loconet/LnConstants.html">LnConstants
class</a> 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.</p>
<h2>Sending and receiving LocoNet messages</h2>
<p>The <a href=
"https://www.jmri.org/JavaDoc/doc/jmri/jmrix/loconet/LocoNetInterface.html">LocoNetInterface</a>
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.</p>
<p><img src="images/LocoNetInterfaceUML.png" alt="UML of JMRI's LocoNet Interface">
</p>
<p>The <a href=
"https://www.jmri.org/JavaDoc/doc/jmri/jmrix/loconet/LocoNetMessage.html">LocoNetMessage</a>
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.</p>
<p>The steps to send a message to the LocoNet are:</p>
<ol>
<li>Create a <a href="https://www.jmri.org/JavaDoc/doc/jmri/jmrix/loconet/LocoNetMessage.html">
LocoNetMessage</a> 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.
</li>
<li>Locate an object providing a <a href=
"https://www.jmri.org/JavaDoc/doc/jmri/jmrix/loconet/LocoNetInterface.html">LocoNetInterface</a>
interface. In many cases, the <a href=
"https://www.jmri.org/JavaDoc/doc/jmri/jmrix/loconet/LnTrafficController.html">LnTrafficController</a>
is providing this, and the object can be located with
<pre>
LocoNetInterface l = LnTrafficController.instance();
</pre><br>
<span class="since">Since <a href="https://www.jmri.org/releasenotes/jmri4.11.6.shtml" target="_blank">JMRI
4.11.6</a></span> The <a href=
"https://www.jmri.org/JavaDoc/doc/jmri/jmrix/loconet/LnTrafficController.html">LnTrafficController</a>
is connected to a given <a href=
"https://www.jmri.org/JavaDoc/doc/jmri/jmrix/loconet/LocoNetSystemConnectionMemo.html">LocoNetSystemConnectionMemo</a>,
and the object can be located with
<pre>
LocoNetInterface l = memo.getLnTrafficController();
</pre>
</li>
<li>Send the message:
<pre>
l.sendLocoNetMessage(msg);
</pre>
</li>
</ol>
<p>Classes that want to receive inbound LocoNet packets should implement the <a href=
"https://www.jmri.org/JavaDoc/doc/jmri/jmrix/loconet/LocoNetListener.html">LocoNetListener</a>
interface, and register their desire to listen via an object with a <a href=
"https://www.jmri.org/JavaDoc/doc/jmri/jmrix/loconet/LocoNetInterface.html">LocoNetInterface</a>
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.</p>
<h2>Implementing the LocoNet connection</h2>
<p>Implementing communication with a real LocoNet is handled by classes that implement the
LocoNetListener interface. There are currently three (see below): <a href=
"https://www.jmri.org/JavaDoc/doc/jmri/jmrix/loconet/LnTrafficController.html">LnTrafficController</a>
and its subclasses <a href=
"https://www.jmri.org/JavaDoc/doc/jmri/jmrix/loconet/LnPacketizer.html">LnPacketizer</a> and
<a href=
"https://www.jmri.org/JavaDoc/doc/jmri/jmrix/loconet/LnTrafficRouter.html">LnTrafficRouter</a>.</p>
<h3>LnTrafficController</h3>
<p>The <a href=
"https://www.jmri.org/JavaDoc/doc/jmri/jmrix/loconet/LnTrafficController.html">LnTrafficController</a>
abstract class provides some common implementation for it's subclasses, and adds a mechanism
to find a usable LocoNetInterface implementation.</p>
<p>The routine <code>addLocoNetListener</code> and <code>removeLocoNetListener</code> methods
are implemented here, along with a <code>notify</code> method to forward LocoNetMessages to
the listeners.</p>
<p>Until JMRI version 4.11.5 the static <code>instance()</code> 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.<br>
See the section on "<a href="#startup">Startup</a>" for more information on this.</p>
<img src="images/LnTrafficControllerUML.png" alt="UML of JMRI's LocoNet Traffic Controller">
<h3>LnPacketizer</h3>
<p>The <a href=
"https://www.jmri.org/JavaDoc/doc/jmri/jmrix/loconet/LnPacketizer.html">LnPacketizer</a> 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 <a href=
"https://www.jmri.org/JavaDoc/doc/jmri/jmrix/loconet/LnPortController.html">LnPortController</a>
- 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.</p>
<p>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.</p>
<h3>LnTrafficRouter</h3>
<p>The <a href=
"https://www.jmri.org/JavaDoc/doc/jmri/jmrix/loconet/LnTrafficRouter.html">LnTrafficRouter</a>
class provides a scatter-gather operation for the LocoNetListener interface. Note that this
implementation doesn't transform the LocoNetMessages into serial traffic.<br>
<img src="images/LocoNetConnections.gif" alt="Drawing of how various objects route messages"
width="640" height="480"><br>
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.</p>
<p>Note that the "some remote comm class" could also be implemented as a subclass of
LnTrafficRouter, instead of communicating with one.</p>
<h2 id="startup">Startup</h2>
<p>Until JMRI 4.11.5 "action" classes connected to an input source. In the current code
<span class="since">Since <a href="https://www.jmri.org/releasenotes/jmri4.11.6.shtml" target="_blank">JMRI
4.11.6</a></span> "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.<br>
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:</p>
<ul>
<li>Typically a LnPacketizer, which becomes the "instance" for objects that use
memo.getLnTrafficController() to locate an interface.</li>
<li>A Programmer instance</li>
<li>A PowerManager instance</li>
<li>TurnoutManager etc. instances</li>
</ul>
These last three provide LocoNet-based services for the higher-level JMRI interfaces (the
<code>instance()</code> method was deprecated to support multiple connections. The
<code>configure()</code> methods in the various adapter classes now handle this.)
<p>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.</p>
<h2>Port adapters</h2>
<a href=
"https://www.jmri.org/JavaDoc/doc/jmri/jmrix/loconet/LnPortController.html">LnPortController</a>
is an abstract base class to carry common implementations for the Adapter classes that
connect to serial ports with specific protocols.
<h4>MS100</h4>
<p>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.</p>
<p>The ConnectionConfig class (in package jmrix.loconet.ms100) starts up a LocoNet connection
via a MS100. When triggered, it creates a visible MS100Frame object.</p>
<p>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.</p>
<p>When the "Open MS100 port" button is pressed, the MS100Frame object</p>
<ul>
<li>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</li>
<li><span class="since">Since <a href="https://www.jmri.org/releasenotes/jmri4.11.6.shtml" target="_blank">JMRI
4.11.6</a></span> then makes sure that a LnTrafficController object exists by calling the
memo.getLnTrafficController() method<br>
(providing multi-connection support and replacing the use of LnTrafficController.instance()
used before)</li>
<li>connects that LnTrafficController instance to the MS100Adapter (subclass of
LnPortController)</li>
<li>starts LnTrafficController in a new thread so that it can handle inbound messages
asynchronously.</li>
</ul>
<h4>LocoBuffer</h4>
<p>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.</p>
<h4>HexFile</h4>
<p>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.</p>
<p>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.</p>
<p>When a file is selected, the HexFileFrame object</p>
<ul>
<li>creates a HexFileAdapter object connected to that file</li>
<li>The HexFileAdapter adapter then creates a new LocoNetSystemConnectionMemo() object,
connects to that port and creates input and output streams</li>
<li><span class="since">Since <a href="https://www.jmri.org/releasenotes/jmri4.11.6.shtml" target="_blank">JMRI
4.11.6</a></span> then makes sure that a LnTrafficController object exists by calling the
memo.getLnTrafficController() method<br>
(providing multi-connection support and replacing the use of LnTrafficController.instance()
used before)</li>
<li>connects that LnTrafficController instance to the HexFileAdapter (subclass of
LnPortController)</li>
<li>starts LnTrafficController in a new thread so that it can handle inbound messages
asynchronously.</li>
</ul>
<p>Unlike the MS100Frame and LocoBufferFrame objects, the HexFileFrame object stays visible
so that it can control the flow of messages from the file.</p>
<h4>LocoBuffer II</h4>
<h4>LocoBuffer USB</h4>
<h4>Uhlenbrock Intellibox I</h4>
<h4>LocoNet over TCP</h4>
<h4>LocoNet RMI Server</h4>
<h4>LocoNet PR2/3/4</h4>
<h4>Uhlenbrock Intellibox-II (USB)</h4>
<h4>DCS52 (USB)</h4>
<h4>DCS240 (USB)</h4>
<h2>Slot and Programmer management</h2>
<p>"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.</p>
<p>The SlotListener interface should be implemented by any class that wants to be notified
when a slot changes.</p>
<p>Because Digitrax command stations handle programming via a special reserved slot, the
<code>jmri.Programmer</code> interface is also implemented by the
<code>loconet.SlotManager</code> class. This greatly complicates the class, but is acceptable
for now.</p>
<p>LocoNet&reg; is a registered trademark of <a href="https://www.digitrax.com">Digitrax,
Inc.</a></p>
<!--#include virtual="/help/en/parts/Footer.shtml" -->
</div>
<!-- closes #mainContent-->
</div>
<!-- closes #mBody-->
<script src="/js/help.js"></script>
</body>
</html>