Java on Tracks

Modellbahn powered by Java EE

Dirk Weil
© Dirk Weil

Wenn man sich professionell mit der Entwicklung von Software befasst und dadurch Tastatur und Monitor tagsüber (und manchmal auch nachts) ständige Begleiter sind, nutzt man IT dann auch in der Freizeit? Man muss nicht, aber man kann – und im hier beschriebenen Fall liegt die Verbindung auch recht nahe: Ich baue und betreibe schon seit einigen Jahren eine Modellbahn im Keller. Die Betonung liegt hier auf „Modell“ als Abgrenzung zu den Spielzeugeisenbahnen, die zumindest in meiner Kinder- und Jugendzeit einige Kinderzimmer schmückten. Um bei der Modellbahn einen vorbildähnlichen Betrieb zu ermöglichen, ist sie mit einer Mehrzugsteuerung ausgestattet, die auch ein Computerinterface aufweist.

Natürlich kann man entsprechende Steuerungssoftware fertig erwerben, aber das ist ja langweilig. Also haben wir uns bei GEDOPLAN für eines unserer Code Camps die Aufgabe gestellt, die Modellbahn mit einer Java-EE-Anwendung zu steuern. Zunächst musste ein Projektname gefunden werden. Die Wahl fiel auf V5T11, was Visual Train Control bedeutet – analog zur bekannten Abkürzung I18n für Internationalization. In anderer Reihenfolge erhält man VT 11.5, was einen Triebzug bezeichnet, der sicher die Herzen vieler Modellbahner höher schlagen lässt.

Aufgabenstellung

Die vollständige Automatisierung des Anlagenbetriebs soll durch V5T11 nicht erreicht werden, schließlich möchte der Modellbahner seine Züge meist selbst aktiv steuern. Vielmehr soll V5T11 die Aufgaben lösen, die beim großen Vorbild im Stellwerk angesiedelt sind: Visualisierung des Gleisplans, Anzeige von Gleisbelegungen sowie Reservieren und Freigeben von Fahrstraßen inklusive der dazu nötigen Weichen- und Signalstellungen.

Für unser Code Camp war es darüber hinaus erwünscht, möglichst viele Teile der Plattform Java EE 6 sinnvoll einzusetzen. Dadurch finden sich in V5T11 neben der auf CDI basierenden Geschäftslogik ein Ressourcenadapter zur Anbindung der Mehrzugsteuerung, eine Webanwendung zur Administration und zum Monitoring, EJBs für die Versorgung eines Remote-Stellwerk-Clients sowie REST-Web-Services für die Fahrzeugsteuerung mit einer Android-App. Im Folgenden soll auf einige Details von V5T11 ein wenig Licht geworfen werden.

Systemaufbau

Zur Steuerung der Fahrzeuge wird eine Mehrzugsteuerung eingesetzt. Anders als bei der Spieleisenbahn benötigt man dann nicht mehrere Stromkreise, auf denen die Loks unabhängig voneinander fahren können. Vielmehr werden alle Fahrzeuge mit der gleichen Dauerspannung versorgt, auf die ein Digitalsignal für Geschwindigkeit, Richtung, Licht etc. aufgeprägt ist. Decoder in den Fahrzeugen setzen die so kodierten Befehle für die Fahrzeuge entsprechend um. In ähnlicher Weise werden Weichen und Signale angesteuert sowie umgekehrt die Belegung von Gleisabschnitten durch Stromfühler gemessen und an eine Zentrale gemeldet. Loks, Weichen, Signale und Gleisabschnitte sind somit an einen Kommandobus angeschlossen. Bei meiner Anlage kommt eine Mehrzugsteuerung des Systems Selectrix zum Einsatz, für das Komponenten von diversen Herstellern erhältlich sind (Abb. 1).

Abb. 1: Komponenten der Mehrzugsteuerung

Mit den beschriebenen Hardware- und Softwarekomponenten ergibt sich der in Abbildung 2 gezeigte schematische Systemaufbau für eine kleine Demonstrationsanlage, die während unseres Code Camps zum Einsatz kam (Abb. 3).

Abb. 2: Systemaufbau

Abb. 3: Demonstrationsanlage

Selectrix-Adapter

Die Kommunikation mit der Mehrzugsteuerung geschieht über ein einfaches serielles Protokoll: Die am Kommandobus angeschlossenen Geräte haben eine Adresse im Bereich 1 … 127. Die Steuerinformationen für eine Lok lassen sich in 8 Bit ausdrücken. An einen Funktionsdecoder sind bis zu acht Weichen oder Signale angeschlossen. Ebenso viele Gleisabschnitte lassen sich mit einem Besetztmelder überwachen. Somit reichen also zur Kommunikation mit der Steuerung jeweils Einzelbytes für Adressen und Daten.

Technisch wird der Datenaustausch mit der Mehrzugsteuerung über einen Ressourcenadapter nach JCA 1.6 abgewickelt. Er stellt der restlichen Anwendung das Interface SelectrixConnection zur Verfügung:

public interface SelectrixConnection extends AutoCloseable
{
  public int getValue(int address);
  public void setValue(int address, int value);
  ...

Die Mehrzugsteuerung kann Änderungen auch selbsttätig melden. Daher verarbeitet der Adapter auch Inbound Messages des Typs SelectrixMessage:

public class SelectrixMessage implements Serializable
{
  private int address;
  private int value;
    ...

Der Ressourcenadapter benötigt für die Kommunikation eine serielle Schnittstelle (für die Jüngeren unter uns: So etwas hatte früher oft einen 9- oder 25-poligen so genannten Sub-D-Stecker und ist heute bspw. per USB-Seriell-Adapter zu bekommen). Leider enthält die Java Runtime von sich aus keine Unterstützung für serielle Schnittstellen. In V5T11 kommt daher die frei verfügbare Bibliothek RXTX [1] zum Einsatz. Sie enthält ein leicht einsetzbares API zum Zugriff auf die Schnittstellen und einen nativen Anteil in Form einer DLL bzw. Shared Library. Dieser native Anteil ist allerdings nicht ganz unproblematisch: Zum einen kann nativer Programmcode bei Auftreten eines Fehlers die vollständige Java-Anwendung zu Fall bringen. Als Teil einer Java-EE-Anwendung könnte also der gesamte Server betroffen sein. Man muss abwägen, ob man das akzeptieren kann. Wir haben es für unsere nicht lebenswichtige Anwendung getan. Zum anderen kann man eine Native Library nur einmal laden. Bei einem Redeployment des Adapters versucht RXTX dies jedoch erneut zu tun, was dann fehlschlägt.

Abhilfe schafft die Auslagerung von RXTX aus der Anwendung heraus in den Server. JBoss basiert seit der Version 7 auf einem Modulsystem, das es erlaubt, Bibliotheken wie RXTX als Modul zur Verfügung zu stellen. Dazu muss die Bibliothek – hier rxtx-2.2-20081207.jar – mit einem proprietären Modul-Descriptor module.xml in einem Verzeichnis unterhalb von jboss-as-7.1.x.Final/modules abgelegt werden (Abb. 4). Der Descriptor gibt dem neuen Modul einen Namen, führt die enthaltenen Bibliotheken auf und könnte darüber hinaus noch Abhängigkeiten zu weiteren Modulen deklarieren. In unserem recht einfachen Fall sieht er so aus:

<module xmlns="urn:jboss:module:1.1" name="org.rxtx.rxtx">
    <resources>
        <resource-root path="rxtx-2.2-20081207.jar"/>
    </resources>
</module>

Abb. 4: Ablage eines Moduls in JBoss 7.1.x

Der Ressourcenadapter kann dann das neue Modul mithilfe des proprietären Deployment Descriptors jboss-deployment-structure.xml (in META-INF) referenzieren:

<jboss-deployment-structure>
  <deployment>
    <dependencies>
      <module name="org.rxtx.rxtx" />
    </dependencies>
  </deployment>
</jboss-deployment-structure> 

JBoss lädt das Modul dann bei Bedarf einmalig. Ein Redeployment des Adapters ist nun unproblematisch.

Betriebssteuerung

Dieser Teil enthält die eigentliche Fachlogik des Systems: Eine umfangreiche baumartige Objektstruktur (Abb. 5) repräsentiert die steuer- und überwachbaren Elemente der Modellbahn (Loks, Weichen, Signale, Gleisabschnitte) sowie die dazu nötigen technischen Bausteine der Mehrzugsteuerung (Zentrale, Funktionsdecoder, Besetztmelder). Dieser Objektbaum wird aus einer XML-basierten Konfigurationsdatei erstellt und mithilfe des Ressourcenadapters ständig mit dem Zustand der Anlage synchronisiert, und zwar inbound durch entsprechende Meldungen des Adapters sowie outbound durch Observer, die Statusänderungen der Objekte als Events verarbeiten.

Abb. 5: Steuerungsobjekt als Repräsentation des Systemzustands

Zur Verankerung dieser Statusverwaltung bietet CDI sehr elegante Möglichkeiten an: Die Bereitstellung des beschriebenen Objekts übernimmt ein Producer, der – mittels @ApplicationScoped als Singleton ausgeprägt – für die einmalige Initialisierung des Objekts in seiner @PostConstruct-Lifecycle-Methode JAXB verwendet (Listing 1).

@ApplicationScoped
public class SteuerungProducer
{
  @Produces @Dependent
  private Steuerung steuerung;
  @PostConstruct
  private void init()
  {
    this.steuerung = XmlConverter.fromXml(Steuerung.class, ...);
    ...
  }

Die Inbound Messages des Adapters werden durch eine Message-driven Bean verarbeitet:

@MessageDriven(messageListenerInterface = SelectrixMessageListener.class)
public class PropagateSelectrixMessageMdb implements SelectrixMessageListener
{
  @Inject
  Steuerung        steuerung;
  public void onMessage(SelectrixMessage message)
  {
    this.steuerung.onMessage(message);

Für die umgekehrte Richtung feuern die Objekte CDI Events, die dann durch eine Observer-Methode an den Adapter weitergeleitet werden (Listing 2).

public class Steuerung
{
  ...
  public void setWert(int adresse, int wert)
  {
    ...
    beanManager.fireEvent(new SelectrixMessage(adresse, wert),
                              new AnnotationLiteral<Outbound>(){});
  }
}

@ApplicationScoped
public class SelectrixGateway
{
  public void setValue(@Observes @Outbound SelectrixMessage selectrixMessage)
  {

Diese lose Kopplung ohne explizite Registrierung des Observers an der Event Source macht es möglich, dass das Statusobjekt auch außerhalb des Servers in der Clientanwendung genutzt werden kann.

Serviceangebot für Clients

Die zentrale Geschäftslogik von V5T11 wird von unterschiedlichsten Clients genutzt: Das Stellwerk greift als Remote-Client von Ferne zu, eine Android-App nutzt REST-Services zur Steuerung von Fahrzeugen, und eine Webanwendung auf Basis von JSF dient dem administrativen Zugriff auf die Steuerung sowie dem rudimentären Monitoring. Um dies möglichst redundanzfrei zu ermöglichen, ist die Betriebssteuerung durch einige Boundaries umgeben, die als Kontaktpunkt für die erwähnten Zugänge dienen (Abb. 6).

Abb. 6: Boundaries für gemeinsame Geschäftslogik

Als Beispiel wird so die Funktionalität „Weiche stellen“ als EJB für den Remote-Zugriff bereitgestellt:

@Stateless
public class SteuerungRemoteServiceBean implements SteuerungRemoteService
{
  @Inject
  Steuerung steuerung;
  @Override
  public void setWeicheStellung(String bereich, String name, Stellung stellung)
  {
    Weiche weiche = this.steuerung.getWeiche(bereich, name);
    weiche.setStellung(stellung);

Die gleiche Funktionalität als REST-Web-Service zeigt Listing 3.

@Path("steuerung/weiche")
public class WeicheWebservice
{
  @Inject
  private Steuerung steuerung;
  @Path("{bereich}/{name}/stellung")
  @POST
  public Response setStellung(@PathParam("bereich") String bereich,
                              @PathParam("name") String name,
                              @FormParam("stellung") Stellung stellung)
  {
    Weiche weiche = this.steuerung.getWeiche(bereich, name);
    weiche.setStellung(stellung);

Analog lässt sich auch ein Presentation Model für eine JSF-basierte Webanwendung bereitstellen. Durch die saubere Schichtung der Anwendung ist es nun also problemlos möglich, verschiedene Clients bzw. Präsentationen auf die gleiche Geschäftslogik zu setzen.

Stellwerk

Die Stellwerksanwendung wurde als Remote Client auf Basis von Swing entwickelt. Sie verwendet das gleiche Statusobjekt, das schon in der serverseitigen Betriebssteuerung Anwendung fand, um den Zustand der Anlage zu visualisieren und interaktiv Änderungen zu initiieren: Weichen und Signale stellen, Fahrstraßen reservieren bzw. freigeben (Abb. 7).

Abb. 7: Stellwerk

Die Statusaktualisierung vom Server zum Client geschieht mittels JMS. Dazu werden Änderungen auf der Serverseite in Form von SelectrixMessage-Objekten in eine Queue eingetragen, die vom Client asynchron verarbeitet wird.

Remote Control

Zur Steuerung der Fahrzeuge verwenden Mehrzugsteuerungen so genannte Remote Controls, mit denen sich mehr oder weniger bequem Fahrzeuge auswählen und in ihrer Geschwindigkeit, Richtung und Beleuchtung steuern lassen. Ältere Geräte sind schnurgebunden, was den Bewegungsradius des Modellbahners etwas einschränkt. V5T11 ermöglicht die Nutzung von Android-Geräten für diesen Zweck. Die entsprechende Anwendung ist allerdings bislang noch sehr rudimentär ausgebildet, was dem verfügbaren Zeitrahmen unseres Code Camps geschuldet ist. Sie nutzt die oben erwähnten REST-basierten Services zur Steuerung der Loks (Abb. 8).

Abb. 8: Android-basierte Remote Control

Zusammenfassung

V5T11 – und damit auch dieser Artikel – hat nicht das Ziel der Erstellung einer vollständigen und umfassenden Modellbahnsteuerung. Vielmehr wollten wir zeigen, dass die sich in einem so maschinennahen Arbeitsbereich stellenden Anforderungen mit Standardmitteln der Plattform Java EE gelöst werden können. Das ist insbesondere durch die Nutzung von CDI als zentralem Dreh- und Angelpunkt für die Geschäftslogik recht elegant gelungen. Dieser Artikel konnte nur Schlaglichter auf die beschriebene Anwendung werfen. Wenn Sie einen tieferen Blick in den Code werfen wollen, können Sie das auf GitHub tun [2].

Geschrieben von
Dirk Weil
Dirk Weil
  Dirk Weil ist seit 1998 als Berater im Bereich Java tätig. Als Geschäftsführer der GEDOPLAN GmbH in Bielefeld ist er für die Konzeption und Realisierung von Informationssystemen auf Basis von Java EE verantwortlich. Seine langjährige Erfahrung in der Entwicklung anspruchsvoller Unternehmenslösungen machen ihn zu einem kompetenten Ansprechpartner und anerkannten Experten auf dem Gebiet Java EE. Er ist Autor in Fachmagazinen, hält Vorträge und leitet Seminare und Workshops aus einem eigenen Java-Curriculum.
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.