JMRI Code: Adding A New System
This page describes the steps to add a new data-type, e.g. Powerline devices, to JMRI.
It uses as an example the addition of the "powerline" type in January 2008. This was before several important migrations (SVN to Git; changes to the internal support for multiple connections; changes to how the Preferences mechanism works; etc) so this may need significant updating.
Some parts may also be useful if you're adding a new connection (e.g. through a network link) to an existing system. If you do that, please mark those sections here for the next person to come along.
Note that we don't do this very often, and the specifics are more important than the generalities here. You should should make sure you understand the intended system connection structure which is documented on another page.
We list the files that are modified and created in the order they were done in this case; other orders may also work, and you might not need to do all these.
In this particular case, we started by copying an existing system implementation (SECSI). This effects how we ordered this, and made the basic process move pretty quickly.
Duplicate an existing system
- Create a duplicate file tree
-
Using whatever tool that useful to you, duplicate the src/jmri/jmrix subtree corresponding to the existing system you want to copy.
- Change the package name information
-
Using your favorite editor, change all the package names in the new files to their new location. In this example, that was a bulk replace of "jmri.jmrix.secsi" with "jmri.jmrix.powerline".
-
At this point, make sure you can compile. This code isn't consistent yet, but it should compile.
- Check copyright dates
-
Because you've copied a system that might not have been touched for a while, go through and add the current year (and if need be, your name) to the copyright notices in all the files.
- Create Git structure
-
In this step, we put the basic Git structure in place.
- Add all the directories you've created to Git. In
the example, this was:
git add powerline powerline/serialdriver powerline/serialmon
git add powerline/configurexml powerline/serialdriver/configurexml - Add all the COPYING files:
git add powerline/COPYING powerline/*/COPYING powerline/*/*/COPYING
- Finally, commmit those files (but only those
files):
git commit -m"usual file" powerline
- Add all the directories you've created to Git. In
the example, this was:
-
At this point, make sure you can compile.
Do Basic Migration
- Migrate names
-
Using your favorite editor, change all occurances of the old system name into the new one.
bbedit `grep -irl secsi powerline/`
Then search for and replace, perhaps not literally, all the occurances.
At this point, make sure you can compile.
- Register system for preferences
-
Add the line
@ServiceProvider(service = ConnectionTypeList)
just before the class definition so that the preferences system sees your new connection types. You may also need to add the following import statements:
import org.openide.util.lookup.ServiceProvider;
import jmri.jmrix.ConnectionTypeList;
- Update the documentation
- Most important, make an entry in the Names.shtml page for the system letter.
- Add at least one page of documentation to the help/en/html/hardware area.
- Link that from the help/en/html/hardware/index.shtml hardware home page. Include a link to the manufacturer's web site if possible.
- Create Basic JUnit Tests
-
For more information on this, see the testing page.
- Duplicate directory
-
Following the steps above, duplicate the directory for the previous systems tests, creating a new directory in test/jmri/jmrix to contain the tests. Change the package names, check the copyright dates, create the Git structure, and migrate the names.
-
At this point, make sure you can compile both the main code (which wasn't changed in this step) and the tests.
You should also be able to successfully run the tests in your new system (although they were created for the old system's functionality):
ant tests &&./runtest.csh jmri.jmrix.powerline.SerialTest
Connect Basics to Rest of System
- Connect to configuration menus
-
Edit the jmri/jmrix/ActiveSystemsMenu.java file (two places), src/jmri/jmrix/JmrixConfigPane.java (one place) and src/jmri/jmrix/SystemsMenu.java (one place) files to add the new system.
Don't commit this to Git yet!
-
At this point, make sure you can compile and run DecoderPro, and that your new system appears in the preferences panel.
- Connect to test tree
-
Edit the test/jmri/jmrix/JmrixTest.java file to invoke your new system tests.
Don't commit this to Git yet!
-
At this point, when you "ant alltest", your new system's test should be run.
Migrate to Basic Functionality
- Update connection options
-
Speeds, etc, in powerline/serialdriver/SerialDriverAdapter.java.
- Get send/receive communications working
-
The first step is to get communications working so that you can send a message from the "Send Command" window, and see it and the response in the "Monitor" window.
Make It Easy to Use
- Add Possible Startup Items
-
Create a new system-specific `ActionListBundle.properties` file (src/jmri/jmrix/powerline/PowerlineActionListBundle.properties) to add appropriate action items. Make sure to update the `getActionModelResourceBundle()` method of the SystemConnectionMemo to reference this newly created ActionListBundle.
Complete the documentation
- Create The Help Tree
-
Start by duplicating, then edit, don't forget to update the index.
You'll also have to change the locations on the various frames
- ant javadoc
-
Create the Javadocs, and fix any new (or old) problems.
- Add the system to the "hardware" web pages
-
Edit help/en/html/hardware/index.shtml
Adding a TCP/IP connection to an existing system
These are rough notes from adding a TCP/IP connection to the RFID type, by copying the architecture of the C/MRI system.- Create a "networkdriver" directory in parallel to the
"serialdriver" directory.
cp java/src/jmri/jmrix/cmri/serial/networkdriver/ java/src/jmri/jmrix/rfid/
- Edit the package and import statements, all files. ".cmri.serial." to ".rfid.", then ".cmri." to ".rfid.".
- Change the "setManufacturer" call in NetworkDriverAdapter.
- C/MRI might not have been the best starting point,
but it's what we've got. Some specific edits needed:
- Change CMRISystemConnectionMemo to RfidSystemConnectionMemo
- Remove the reference to NodeConfigAction in ConnectionConfig (or use this as the base for any special configuration you need; it was adding a "Configure Nodes" button)
- Change the name of SerialSensorManager throughout
- Remove the SerialTurnoutManager SerialLightManager references (there aren't any in RFID)
- Add RfidReporterManager
- Needs a network driver:
cp java/src/jmri/jmrix/cmri/serial/SerialNetworkPortController.java java/src/jmri/jmrix/rfid/RfidNetworkPortController.java
and edit as above. - java/src/jmri/jmrix/rfid/networkdriver/configurexml/ConnectionConfigXml.java contains dead code for configuring the C/MRI nodes (again, maybe not the right system to copy from). Leave that for later by commenting out the body of extendElement(Element e) and unpackElement(Element e).
- Change references to SerialTrafficController to RfidTrafficController.
- Another problem with C/MRI as a prototype is that it
still has an instance() call in NetworkDriverAdapter. Go
to the RFID (multi-system capable) SerialDriverAdapter
and copy the structure there.
// connect to the traffic controller this.getSystemConnectionMemo().setRfidTrafficController(control); control.setAdapterMemo(this.getSystemConnectionMemo()); control.connectPort(this); control.sendInitString(); // declare up jmri.jmrix.rfid.ActiveFlag.setActive();
- Add jmri.jmrix.rfid.networkdriver.ConnectionConfig to java/src/jmri/jmrix/rfid//RfidConnectionTypeList.java
- Change "RFID Device Connection" to "Direct Serial Connection" in ConnectionConfig.name() The network connection will default to "Network Connection".
- Check that configure/save/restart brings back all the configuration options. It should, you copied the necessary code above, but check.
Connections with Complicated Setup
Some connection types need more configuration and/or setup than you can get with the usual option1/option2/option3 mechanism. Tools available are:- loadDetails() in ConnectionConfig. This is invoked when setting up the preferences window, and can be used to add additional controls. C/MRI uses this to add a "Configure Nodes" button. RFID uses it to add listeners that control which combinations of options are available.
- extendElement(Element e) and unpackElement(Element e) in ConnectionConfigXml can be used to store additional information and reload it, respectively. C/MRI uses this to store its node information.