Code documentation

Development tools

Code Structure

Techniques and Standards

How To

Functional Info

Background Info

JMRI Code: Structure of External System Connections

This page is about how JMRI connects to external systems, e.g. DCC systems.

There's a lot of variation within JMRI on this, so you'll have to go through any specific implementation. Specifically, older systems weren't always arranged this way, so existing code may not be a good example.

See also the Multiple Connection Update page.

Code Structure

The code for a general type, like "LocoNet connections" or "NCE connections", should be gathered in a specific package right under jmri.jmrix e.g. jmri.jmrix.loconet and jmri.jmrix.nce. In the preferences dialog and JmrixConfigPane main configuration code, this level is called the "manufacturer selection". It provides a level of grouping, which we may someday want to use for e.g. providing separate updates for specific hardware, while still separating the system-specific code from the system-independent parts of JMRI.

Within that, the code should be separated further by putting specific hardware options into their own subpackages, for example

In the preferences, this is called the "connection mode" selection.

Additional subpackages can be used grouping various functions as needed. For example, Swing-based tools should go in their own swing subpackage or at a further level within the swing subpackage.

Normal Operation

The key to normal operation (after start up and before shut down) is a SystemConnectionMemo object that provides all necessary access to the system connection's objects. For example, the LocoNetSystemConnectionMemo provides access to a number of LocoNet-specific objects and LocoNet-specific implementations of common objects. Although some of those (e.g. a SensorManager) might be separately available from the InstanceManager, accessing them from a SystemConnectionMemo allows you to find the consistent set associated with one specific connection of a multiple-connection setup, even when there are multiple connections of a specific type. There are also a few tools that work with the SystemConnectionManager objects themselves after obtaining them from the InstanceManager.

Initialization

We don't directly persist the SystemConnectionMemo. This is partly for historical reasons, but it also reflects the level of abstraction: A SystemConnectionMemo is at the level of a "LocoNet connection" or a "NCE connection", and there's a lot of specific information below it to configure one of many possible such connections.

Instead, configuration of the connection is from the bottom up: From the most specific code up to the general. The "Adapter" object connects directly to the system, e.g. managing a serial link, and then builds up the objects that work with that link, including all the various type managers. This makes sense because the type of the connection is really specified via the type of that link and what's on the other end of it.

"Simple" Initialization Sequence

This section describes the LocoNet implementation of the new (post-multiple) configuration system. This is similar for LocoBuffer, LocoBuffer-USB, PR3, etc connections, but we use the specific LocoBuffer-USB case for concreteness. This sequence picks up after the basic startup of the application itself, see the App Structure page for that.

There are several objects involved in startup:

The profile XML file contains a connection element that drives the configuration:

        <connection xmlns="" class="jmri.jmrix.loconet.locobufferusb.configurexml.ConnectionConfigXml"
                                disabled="no" manufacturer="Digitrax" port="/dev/tty.usbserial-LWPMMU13"
                                speed="57,600 baud" systemPrefix="L" userName="LocoNet">
            <options>
                <option>
                    <name>CommandStation</name>
                    <value>DCS50 (Zephyr)</value>
                </option>
                <option>
                    <name>TurnoutHandle</name>
                    <value>Normal</value>
                </option>
            </options>
        </connection>
      
Initialization proceeds through multiple steps (click on the diagram to expand it):

Lessons

Should this part move up?

More Complex Initialization: C/MRI

For a more complex example, consider C/MRI, which has more content in it's <connection>element:
  <connection userName="C/MRI" systemPrefix="C" manufacturer="C/MRI"
                    disabled="no" port="(none selected)" speed="9,600 baud"
                    class="jmri.jmrix.cmri.serial.sim.configurexml.ConnectionConfigXml">
    <options />
    <node name="0">
      <parameter name="nodetype">2</parameter>
      <parameter name="bitspercard">32</parameter>
      <parameter name="transmissiondelay">0</parameter>
      <parameter name="num2lsearchlights">0</parameter>
      <parameter name="pulsewidth">500</parameter>
      <parameter name="locsearchlightbits">000000000000000000000000000000000000000000000000</parameter>
      <parameter name="cardtypelocation">1122221112000000000000000000000000000000000000000000000000000000</parameter>
    </node>
    <node name="1">
      <parameter name="nodetype">1</parameter>
      <parameter name="bitspercard">24</parameter>
      <parameter name="transmissiondelay">0</parameter>
      <parameter name="num2lsearchlights">0</parameter>
      <parameter name="pulsewidth">500</parameter>
      <parameter name="locsearchlightbits">000000000000000000000000000000000000000000000000</parameter>
      <parameter name="cardtypelocation">2210000000000000000000000000000000000000000000000000000000000000</parameter>
    </node>
    <node name="2">
      <parameter name="nodetype">2</parameter>
      <parameter name="bitspercard">32</parameter>
      <parameter name="transmissiondelay">0</parameter>
      <parameter name="num2lsearchlights">0</parameter>
      <parameter name="pulsewidth">500</parameter>
      <parameter name="locsearchlightbits">000000000000000000000000000000000000000000000000</parameter>
      <parameter name="cardtypelocation">2212120000000000000000000000000000000000000000000000000000000000</parameter>
    </node>
    <node name="4">
      <parameter name="nodetype">1</parameter>
      <parameter name="bitspercard">24</parameter>
      <parameter name="transmissiondelay">0</parameter>
      <parameter name="num2lsearchlights">0</parameter>
      <parameter name="pulsewidth">500</parameter>
      <parameter name="locsearchlightbits">000000000000000000000000000000000000000000000000</parameter>
      <parameter name="cardtypelocation">2210000000000000000000000000000000000000000000000000000000000000</parameter>
    </node>
  </connection>
How this gets read in.

Implications for internal structure.

Proper internal structure

Configuration Process

See jmrix.JmrixConfigPane Javadoc for links to configuration elements. (Is there another place that the configuration process and preferences support is described? If so, it should be linked from here.)

Any particular system connection is included in the preferences by being listed in the java/src/META-INF/services/jmri.jmrix.ConnectionTypeList list. This file is normally generated from the @ServiceProvider(service = ConnectionTypeList) class-level annotations.

# Providers of System Connections type lists in Preferences
# Order is Insignificant
jmri.jmrix.internal.InternalConnectionTypeList
jmri.jmrix.lenz.LenzConnectionTypeList
...
jmri.jmrix.loconet.LnConnectionTypeList
...
      
This provides the contents for the 1st-level selection in the top JComboBox, e.g. in this case "Digitrax". This (generally) corresponds to selecting a system package within the JMRI package that might contain multiple varients of a specific connection. Within JmrixConfigPane this is called the "manufacturer" selection.

The contents of the jmri.jmrix.loconet.LnConnectionTypeList, an instance of jmri.jmrix .ConnectionTypeList then provides the contents for the second-level JComboBox of specific connection types, each corresponding (generally) to a specific ConnectionConfig implementation that can configure a specific connection type. Within JmrixConfigPane this is called the "mode" selection.

Creating from scratch

Note this starts off by creating a ConnectionConfig, which creates a PortAdapter, similar to the read-from-XML version. But we don't want a running connection: We want one that we can work with to set/store configuration information. So, although we "register()", we do not "configure()".

Filling the details JPanel is done within the ConnectionConfig object via a call to loadDetails(). In many cases, including this LocoNet example, that's referred up to a base class:

Storing

Changing Options

The Swing panel that shows the main options (e.g. option1 through option4) sets changes to those values directly into the ConnectionConfig/PortAdapter without asking them to act further via e.g. configure().

Updating a Connection Mode

Changing the mode JComboBox in JmrixConfigPane first clears the existing contents of the details JPanel with removeAll(), then calls the JmrixConfigPane.selection() method to refill it.

Deleting a Connection

Misc

This section is a grab-bag of other things you might want to know about the system connection structure.