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, user names can not have leading or trailing spaces. If leading or trailing spaces are entered, they'll be "trimmed" off.There are lots of things that might need names, such as Turnouts, Sensors, and other layout elements. 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. A list of named types follows below.
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.
Examples:
Note: These assume the default values of connection prefix letters, but they certainly could have been defined differently, including using lower case and numbers.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 a lower case or upper case letter followed by digits, e.g. "N1" and "n2".
Note:
There are three special cases. These are JMRI NamedBean types that are not connected to any single system connection, so they always use a particular system letter prefix and type letter regardless of whether that prefix has been configured to a connection: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 connection 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.
"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 connection 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)
When you add an item to one of the tables, many times you only have to enter the numbers
(the suffix) and JMRI will 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: 7 | |
DigiXbee | o | 4:3 | ModuleAddress:Pin | ZS4:3 | int : int | pin: 0 | pin: 7 | |
DigiXbee | o (Turnouts) | 4:3:4 | ModuleAddress:Pin1:Pin2 | ZT4:3:4 | int : int : int | pin: 0 | pin: 7 | |
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 | ||
Internal | i/o | some string |
whatever meaning the user wants to assign; see below for special cases |
ITsome string | ||||
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 | ||
CBUS | io | 18 | Event 18 On; 18 Off | MT+18 | integer | +18;-18 | 01 | 65535 |
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 | ||
CBUS | i | 200018M07 | listen to Events 18 .. 1F | MS200018M07 | + M + hex mask | N/A | ||
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 | |
CBUS | io | +18;+21 | Event 18 On; 21 On | MT18;21 | integer ; integer | +18;+21 | 1;1 | 65535;65535 |
CBUS | io | +18;-21 | Event 18 On; 21 Off | ML+18;-21 | idem signed | N/A | 1;1 | 65535,65535 |
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 | 99 | ||
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 |
SENSOR GROUP:my group:LS1 SENSOR GROUP:my group:LS2are 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:
:
(colon, ASCII 0x3A), "
(double
quote, ASCII 0x22), nor $
(dollar sign, ASCII 0x24) characters in their system
or user names. Automatic tools should use at least one of these to make sure they don't
collide with user-selected names. Quotes should always be used in pairs to allow
nesting.The list of tools currently working this way is:
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);
Please use that, and only that, when creating a user name from human input or other source.
Do not explicitly call String#toUpper()
, String#strip()
or any
other formatting operation; having those spread across the code is unmaintainable.
Because system names may vary from type to type and manager to manager, there are two
manager-specific methods of interest: manager.validateSystemNameFormat(input,
locale)
and manager.makeSystemName(input)
. Since most NamedBeans are
created by managers, and managers know about the complete set of NamedBeans, that's the right
place to put them. But it means that an individual NameBean, i.e.
jmri.jmrix.internal.InternalTurnout
, constructor can't access it. If one of
those constructors discovers it can't parse a provided system name argument because it's
invalid, it should throw a NamedBean.BadSystemNameException
. The constructor
should not in any way transform the system name that it was given. It has to use it as given
or, if it can not, throw the exception.
For more information, see the Javadoc for normalizeUserName, validateSystemNameFormat, makeSystemName, BadUserNameException, and BadSystemNameException.
In general, it's better to use an input method that already handles all this. Two are available now:
import jmri.swing.NamedBeanComboBox; // the first argument is the manager // the 2nd is a bean to select or null // the third determines how the beans are displayed selectComboBox = new NamedBeanComboBox(InstanceManager.getDefault(SensorManager.class), currentSensor, NamedBean.DisplayOptions.DISPLAYNAME) // to get the selected item: selectComboBox.getSelectedItem()
import jmri.jmrit.picker.*; PickListModel tableModel = PickListModel.sensorPickModelInstance(); // or another type PickSinglePanel panel = new PickSinglePanel(tableModel); // to get the selected item as a NamedBeanHandle<T> panel.getSelectedBeanHandle(); // you can listen for a selected value by // inheriting from ListSelectionListener // and registering with // panel.getTable().getSelectionModel().addListSelectionListener(this) @Override public void valueChanged(ListSelectionEvent e) { System.out.println(" Selected: "+panel.getSelectedBeanHandle()); }
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:
NamedBeanComparator
- a comparison on the NamedBeans themselvesSystemNameComparator
- a comparison on Strings (deprecated)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.
NamedBeanComparator
is the standard method going forward as it can do
type-specific sorting of the suffix part of the names: It knows what the C/MRI suffixes in
"CT2003" and "C2B2" mean and can take that into account.
Because it works with String values, SystemNameComparator
could only do a
default ordering of the system-specific suffix; it couldn'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.
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.
CBUS® is a registered trade mark of Dr Michael Bolton.