Today I finished a library for the Roco/Fleischmann Z21 in Python. In previous posts I already showed how to connect to the Z21, read feedback messages and send commands for controlling trains and automated shunting. When discussing code quality, I looked into refactoring and improving the code examples.
I reworked the examples and added more functions that I consider essential for using the Z21 with Python. This resulted in the Z21 Library that I’m offering for free to all subscribers!
The library follows Roco’s documented protocol tightly. The latest version of the protocol is 1.09 at the time of writing, and Roco has finally released an English version of the document. You can find it here.
The library includes all functions in chapters:
- 2. System, status, versions
- 4. Driving
- 5. Switching
- 7. Feedback – R-bus
These are the functions that I consider most relevant for automated train control. Other functions are more specialistic or not interesting for automating. These are chapters:
- 3. Settings of locomotives and switches
- 6. Reading and writing decoder CV’s
- 8. RailCom
- 9. LocoNet
- 10. CAN
I might implement these functions at a later time. Or, you could add them yourself once you understand how the library works.
I did not write the library just for having the functions implemented. I also wanted to elaborate on the refactoring exercises that I presented before. My aim was to apply functional programming as much as possible.
Think of this library is a facade for the Z21 Command Station. The design pattern I applied is Singleton since I assume that only one command station is to be used. As such the library is a set of individual functions rather than a class. Only the address of the Z21 is specified as a global constant. It contains the default settings from the factory. If you have changed the configuration, you’ll need to update this in the library.
I approached the creation of outgoing messages and handling the incoming messages as transformations. Each transformation is implemented as a pure function: only local variables that are not mutated during their life span. No states, no global variables and no side effects. I also tried to express IF statements as a ternary operator as much as possible.
The functions are reasonably short. I did use more variables than strictly necessary, to make the protocol document easier to follow and minimise the need for comments in the code.
All functions that interpret incoming records have correctness checks. This may be unnecessary assuming that the Z21 will not send any incorrect messages, and the functions handling incoming records don’t contain any bugs. Still, in the event of an unexpected error happening, I prefer to have an error reported rather than the application crashing.
The library contains instructions how to connect to the Z21, and use the functions to actually perform send and receive operations. Of course, I/O operations are not stateless and therefore cannot be pure in the sense of functional programming.
All functions I tested on a life track with a locomotive. One could call this system testing. I built up the dispatch functions in a TDD fashion. The functions for breaking down the incoming packets into records were a modified version of the ones I built and tested in this post.
I felt there was no need for writing features, user stories and scenarios. We won’t need any automated unit tests because of the simplicity of most functions and also the need for regression testing in the future will likely be minimal. This because we can expect that future versions of the Z21 firmware will merely add new functionality, rather than changing existing. Likewise, future versions of Python will likely not deprecate functionality that is used in this library.
So subscribe to this blog to receive the library for free! And feel free to give feedback on the code quality, scope of the library or if you happen to find any bugs.