Code documentation

Development tools

Code Structure

Techniques and Standards

How To

Functional Info

Background Info

JMRI: Names and Naming

This page discusses how JMRI objects are named, how those names are used to reference the objects (hardware and software), and how user-readable names are used.

Since JMRI 4.7.4 As of March 2017, we have changed the allowable user names: They can't have leading or trailing spaces. If leading or trailing spaces are entered, they'll be "trimmed" off.

What's in a name?

Why do we need names at all, rather than just references within the code? There are several important uses:
  1. When working with user input, e.g. typing a number in a field, the code will need to know how to map the user-provided info onto objects. Sometimes the user will want to name objects with arbitrary user names, e.g. "East Lockport Turnout". These can't be known until their mapping to hardware has been made. But other times these will be de-novo identifications that the code must understand, e.g. something that means "LocoNet Turnout 23".
  2. We imagine that configurations will be stored in XML files, in a symbolic form. Names are a convenient way to connect objects and object references in that kind of configuration.

Items with names

There are lots of things that might need names: Some of these are associated with a specific hardware device, e.g. a particular turnout. Others are more virtual, e.g. a route, which is a collection on control information within the program.

System and User Names

JMRI users both "system names" and "user names" to reference things.

We want users to be able to call things what they want. Names like "p(24,23)*" are not useful. Every named item can therefore have a "user name", which is an entirely free-form string. You can put whatever you want in there, so long as it's not a duplicate of the user name given to something else. For example, you might call a Turnout "West Yard Lead" or "Turnout 32" or "Green Wire from Controller" or whatever.

At the same time, we need a shorthand name, really a unique identifier, to talk about specific objects. This doesn't have to be convenient, but does have to have a clear mapping from name to object and back. For example, we need a very specific way to identify "LocoNet Turnout 23". We call these "system names". JMRI code will map these to and from whatever information the hardware may need.

System Name Format

A system name is formed from a short "system prefix" representing the hardware system, followed by a single upper case "type letter" indicating the type of the object, followed by a system- and type-specific "suffix string" identifying a specific object.

Examples:

Note: These assume the default values of system prefix letters, but they certainly could have been defined differently. Note that there is no assumption of pattern to the names; they don't have to be assigned monotonically, nor are they restricted to a single system.

System Prefix

Originally, the "hardware prefix" was a single uppercase letter identifying a single system connection: L for LocoNet, N for NCE, etc. The default letters for those are listed below. This is still by far the most common use: Most model railroads have a single connection, and just use the default letter.

The JMRI code is much more flexible than that, however. This allows it to deal with multiple system connections and overlaps of letters (such as the multiple possibilities defined for "D" or "M" below). You change the letter associated with a system connection in the preferences to any other uppercase or lowercase letter. You can call your NCE connection "P" if you want to. If you have two of them, you can call one "X" and the other "Y". You can also use an upper case letter followed by digits, e.g. "N1" and "N2".

Default System Letters

Note that some of these are placeholders, and have no underlying implementation. (Links are to JMRI pages with more information)

Also note that some older implementations used formats that don't meet the current standard, with system letters such as "DX", "DCCPP", "DP", "MR", "MC", "PI", "TM". These need to be migrated, and we have a have a process in place to do that.

Device type letters

Note that some of these are placeholders, and have no underlying implementation. Also, there is no guarantee that any given hardware system will ever implement all of them.

System-specific suffix

"Internal" objects can also be addressed and manipulated, but they don't have a strict correspondence with some hardware on the layout. For example, if a signal head is implemented as three different outputs, LT1, LT2 and LT3, the signal head object might be called IH3.

Each different hardware system can specify the "suffix string" that follows the system and type letters. Generally, these are small numbers, but their exact meaning is very system-specific. For more information, please see the specific pages for

(If you find any missing or see omissions in the following summary, please add a reference)

Adding an item to the table - Entry Format Summary

When you add an item to one of the tables, many times you only have to enter the numbers and have JMRI construct the complete system name.
Here's a summary of the options per Connection, split up for outputs (eg. Turnouts) and inputs (eg. Sensors):

Connection In/Out Entry Meaning makes System Name Mask Equivalent Minimum Maximum
C/MRI i/o 1003 Node 1, Input 3 CS1003 n digits (node) + 3 digit (pin) 1:3 node: 1; pin: 1 node: 127; pin: 999
C/MRI o 3 Node 0, Output 3 CT3 1 999
C/MRI i/o 4003 Node 4, Output 3 CT4003 n digits (node) + 3 digit (pin) 4:3 node: 1; pin: 1 node: 127; pin: 999
C/MRI i/o 4:3 Node 4, Output 3 CT4:3 4003 0:1 127:999
C/MRI i/o 4B3 Node 4, Output 3 CT4B3 4003 0B1 127B999
DCC++ i 4:3 (converts to 50) DT50 node : pin 0
DCC++ o 12 ID in internal DCC++ table DT1212 integer 0 32767
DigiXbee i 4:3 ModuleAddress:Pin ZS4:3 int : int pin: 0 pin: 71(?)
DigiXbee o 4:3:4 ModuleAddress:Pin1:Pin2 ZT4:3:4 int : int : int pin: 0 pin: 71(?)
Grapevine i 22016 Sensor node 22, pin 16 GS 22 016 n digits (node) + 3 digit (pin) node: 1; pin: 001 node: 127; pin: 016
Grapevine i 22p16 p = parallel input GS 22 p16 int + p + int (pin) p1 p16
Grapevine i 22a3 a = ASD occupancy sensor GS 22 a3 int + a + int (pin) 22103 a1 a24
Grapevine i 22103 a = ASD occupancy sensor GS 22023 int + a + int (pin) 22a3 101 124
Grapevine i 22s3 s = old style serial occupancy sensor GS 22 s3 int + s + int (pin) 22023 s1 s16
Grapevine i 22023 s = old style serial occupancy sensor GS 22 s3 int + s + int (pin) 22a3 021 036
Grapevine i 22m3 m = ASD motion sensor GS 22 m3 int + m + int (pin) 22203 1 24
Grapevine i 22203 m = ASD motion sensor GS 22 203 22m3 201 224
Grapevine o 22103 output, card/bank 1, connector 3 GT 22 103 101/201/301/401 124/224/324/424
LocoNet i 34 Sensor 34 LS34 integer N/A 1 4096
LocoNet o 34 Turnout 34 LT34 integer N/A 1 4096
Maple i 2010 Node 2 Input bit 10 KS2010 1 1000
Maple o 1016 Node 1 Output (Turnout) 16 KT1016 1 8000
MERG-CBUS io 18 Event 18 On; 18 Off MT+18 integer +18;-18 01 65535
MERG-CBUS io +N2E18;-N2E18; Node 2 Event 18; On Event = Active; Off Event = Inactive MS+N2E18;-N2E18 Node 1 Event 1 Node 65535 Event 65535
MERG-CBUS i 200018M07 listen to Events 18 .. 1F MS200018M07 + M + hex mask N/A
MERG-CBUS io X9000020012;X91FFFFFFFE hex CAN frame msg. Active; Inactive
N2 E18 active; N65535 E65534 inactive
MSX9000020012;X91FFFFFFFE hex ; hex N/A Depends on Opscode
MERG-CBUS io +18;+21 Event 18 On; 21 On MT18;21 integer ; integer +18;+21 1;1 65535;65535
MERG-CBUS io +18;-21 Event 18 On; 21 Off ML+18;-21 idem signed N/A 1;1 65535,65535
MERG-CBUS io 200018 Node 2 Event 18; On Event = Active; Off Event = Inactive MS+200018 node + (5 digits) N2E18 100001 6553565535
NCE i 4:3 AIU Cab 4, pin 3 NS50 cab: 1; pin: 1 cab: 63; pin: 14
NCE i 50 AIU Cab 4, pin 3 NS50 cab: 1; pin: 1 cab: 63; pin: 14
NCE o 16 Output (Turnout) 16 NT16 1 2044
TMCC (Lionel) o 16 Output (Turnout) 16 NT16 1 511
X10 o A3 House code A + num device code PTA3 caps letter + num house code: A; device: 1 house code: P; device: 16
X10 Insteon o 01.2A.B4 Light (module) PL01.2A.B4 PL01.2A.B4 3 x 2 chars not documented
XpressNet i 3 Feedback module 1, input 3 XS3 1 1024
XpressNet i 99:3 Feedback module 99, input 3 XS787 1 1024
XpressNet o 3 Turnout 3 XT3 1 1024

Naming Conventions For Automated Use

Some higher-level constructs create their own items. For example, a "Sensor Group" is really just a collection of Routes that implements the sensor group logic; there is no specific object in the program that implements the sensor group. Instead, when the user creates sensor group "my group", a series of routes with system names like:
SENSOR GROUP:my group:LS1
SENSOR GROUP:my group:LS2
are created which implements the group. The sensor group tool knows to look for routes of this name.

To make this possible, two informal rules are used:

The list of tools currently working this way is:

Notes

For Programmers

Normalized form of Names, and how to work with it

After multiple long discussions, we decided that both system and user names should be kept in a "normal" form which enforces certain restrictions. For user names, that's that leading and trailing whitespace (spaces, tabs) are not allowed. For system names, it depends on the system, but at least the system-prefix and type letter must be correct.

The code to make sure that names are in normal form has been localized to a single routine for user names:

          String userName = NamedBean.normalizeUserName(input);

Because system names may vary from type to type and manager to manager, this is manager-specific for them:
          String systemName = manager.normalizeSystemName(input);

For more information, see the Javadoc for normalizeUserName and normalizeSystemName

In general, it's better to use an input method that already handles all this. Two are available now:

System Name Comparisons

System names are compared and sorted in multiple places: as labels for table rows, in selection boxes and lists, etc. We have two java.util.Comparator<> implementations to handle this: Please use one of these whenever you need to compare or sort by system names to keep the complexity in one place.

Both of them sort first by system connection prefix, to group all the objects from one system together. If there are objects of multiple types, the type letter is put in alphabetical order next. Finally, the system-specific suffix is sorted.

Because it works with String values, SystemNameComparator can only do a default ordering of the system-specific suffix; it can't do anything that uses any information about the system-specific meaning of that string. It therefore uses an alphanumeric-by-chunks sort. Because it's comparing on the actual NamedBean objects, NamedBeanComparator can do more specific comparisons: It knows what the C/MRI suffixes in "CT2003" and "C2B2" mean and can take that into account.

Therefore, if you can, do the sort/comparison with NamedBeanComparator and then convert to Strings, rather than converting NamedBeans to Strings and using SystemNameComparator.

Also, if you've created a system that has complex information in the suffix, please have your NamedBean subclasses implement a system-specific version of the compareSystemNameSuffix() method in NamedBean. For an example, see the bottom of jmri.jmrix.cmri.serial.SerialTurnout and jmri.jmrix.cmri.serial.SerialTurnoutTest.