Wie schon gesagt, von PluginClient abgeleitet, kümmert sich diese Klasse um die Daten, die vom Sim kommen.

Primär legt sie alles in Listen ab, die dann von der wand-Klasse abgeholt werden können. Weiteres inline in dem Kommentaren.

connection.java
/*
 */
package js.java.plugins.monitorWand;
 
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.TreeMap;
import java.util.TreeSet;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JMenu;
import js.java.stspluginlib.PluginClient;
 
/**
 *
 * @author js
 */
public class connection extends PluginClient
{
 
    /**
     * Behälter für die Züge und ihren Fahrplan
     */
    private static class ZugDetailFahrplan
    {
 
        final public int zid;
        final public String name;
        public int verspaetung;
        public String gleis;
        public String plangleis;
        final public String von;
        final public String nach;
        public boolean sichtbar;
        public LinkedList<ZugFahrplanZeile> plan = null;
 
        public ZugDetailFahrplan(ZugDetails reference)
        {
            zid = reference.zid;
            name = reference.name;
            verspaetung = reference.verspaetung;
            gleis = reference.gleis;
            plangleis = reference.plangleis;
            von = reference.von;
            nach = reference.nach;
            sichtbar = reference.sichtbar;
        }
 
        public String getFormattedAn()
        {
            try
            {
                return plan.getFirst().getFormattedAn();
            }
            catch (NullPointerException e)
            {
                return "";
            }
            catch (NoSuchElementException e)
            {
                return "";
            }
        }
 
        public String getFormattedAb()
        {
            try
            {
                return plan.getFirst().getFormattedAb();
            }
            catch (NullPointerException e)
            {
                return "";
            }
            catch (NoSuchElementException e)
            {
                return "";
            }
        }
 
        public long getAb()
        {
            try
            {
                return plan.getFirst().ab;
            }
            catch (NullPointerException e)
            {
                return 0;
            }
            catch (NoSuchElementException e)
            {
                return 0;
            }
        }
 
        public boolean update(ZugDetails other)
        {
            boolean ret = !(plangleis.equals(other.plangleis));
            verspaetung = other.verspaetung;
            sichtbar = other.sichtbar;
            gleis = other.gleis;
            plangleis = other.plangleis;
            return ret;
        }
    }
    /**
     * das Menü für die Bahnsteigliste
     */
    private final JMenu menu;
    /**
     * die Bahnsteigliste
     */
    private final TreeMap<String, JCheckBoxMenuItem> bahnsteige = new TreeMap<String, JCheckBoxMenuItem>();
    /**
     * Name des Stellwerks
     */
    private String name = "";
    /**
     * Listener für die Menu-Auswahl
     */
    private ActionListener menuListener = new ActionListener()
    {
 
        @Override
        public void actionPerformed(ActionEvent e)
        {
            repaint();
        }
    };
    /**
     * Timer-Listener, um alle 30 Sekunden die Zugdaten zu aktualisieren
     */
    private ActionListener refreshListener = new ActionListener()
    {
 
        @Override
        public void actionPerformed(ActionEvent e)
        {
            request_zugliste();
        }
    };
    /**
     * der Timer zum refreshListener
     */
    private javax.swing.Timer refreshTimer = new javax.swing.Timer(1000 * 30, refreshListener);
    /**
     * Liste der Züge und ihrer Fahrpläne
     */
    private HashMap<Integer, ZugDetailFahrplan> zuglist = new HashMap<Integer, ZugDetailFahrplan>();
 
    connection(JMenu m)
    {
        /**
         * PluginClient verlangt 4 Parameter im Konstruktor:
         * - Name des Plugins (MonitorWand)
         * - Autor (JS)
         * - Version des Plugins (0.9)
         * - eine kurze Beschreibung des Plugins (Demo-Abfahrtwand)
         */
        super("MonitorWand", "JS", "0.9", "Demo-Abfahrtwand");
        this.menu = m;
    }
 
    /**
     * Wird von PluginClient aufgerufen, wenn die Verbindung zum Sim erfolgreich hergestellt
     * wurde. Zu dem Zeitpunkt wurde sich auch schon registriert. 
     * Die Simzeit ist erst nach diesem Aufruf verfügbar!
     */
    @Override
    protected void connected()
    {
        this.request_setdebug(true);    // Debugmeldungen in der Sim-Console ein
        this.request_anlageninfo();
        this.request_bahnsteigliste();
        refreshTimer.setInitialDelay(1000);
        refreshTimer.start();
    }
 
    protected void repaint()
    { // tut nichts
    }
 
    /**
     * Wird vom PluginClient aufgerufen, wenn die Verbindung beendet wurde. Die Ursache ist dabei egal.
     */
    @Override
    protected void closed()
    {
        System.exit(100);
    }
 
    /**
     * Antwort vom <anlageninfo>
     * @param aid
     * @param name
     * @param build
     */
    @Override
    protected void response_anlageninfo(int aid, String name, String build)
    {
        this.name = name;
        repaint();
    }
 
    /**
     * Antwort vom <bahnsteigliste>
     * @param bl <Bahnsteigname,Set<Nachbar>>
     *
     * Baut das Menü mit den Bahnsteigen (neu) auf
     */
    @Override
    protected void response_bahnsteigliste(final HashMap<String, HashSet<String>> bl)
    {
        synchronized (bahnsteige)
        {
            bahnsteige.clear();
            for (String s : bl.keySet())
            {
                bahnsteige.put(s, null);
            }
        }
        javax.swing.SwingUtilities.invokeLater(new Runnable()
        {
 
            @Override
            public void run()
            {
                menu.removeAll();
                synchronized (bahnsteige)
                {
                    for (Map.Entry<String, JCheckBoxMenuItem> s : bahnsteige.entrySet())
                    {
                        JCheckBoxMenuItem m = new JCheckBoxMenuItem(s.getKey());
                        m.setSelected(true);
                        m.addActionListener(menuListener);
                        menu.add(m);
                        s.setValue(m);
                    }
                }
                repaint();
            }
        });
    }
 
    /**
     * Antwort vom <zugliste>
     * @param zl <zid,name>
     *
     * speichert neue Züge in einer Liste, fragt für jeden Zug die Details ab
     */
    @Override
    protected void response_zugliste(HashMap<Integer, String> zl)
    {
        for (Integer zid : zl.keySet())
        {
            if (!zuglist.containsKey(zid))
            {
                zuglist.put(zid, null); // noch unbekannter ZID, neu aufnehmen
            }
            this.request_zugdetails(zid); // Details immer abfragen, Verspätungen, Gleisänderung ändern sich ständig
        }
        Iterator<Integer> it = zuglist.keySet().iterator();
        while (it.hasNext())
        {
            int zid = it.next();
            if (!zl.containsKey(zid))
            {
                it.remove(); // aufräumen, ZIDs, die nicht mehr mitgeschickt wurden, gibt es nicht mehr
            }
        }
    }
 
    /**
     * Antwort vom <zugdetails zid>
     * @param zid
     * @param details
     */
    @Override
    protected void response_zugdetails(int zid, ZugDetails details)
    {
        ZugDetailFahrplan zd = zuglist.get(zid);
        if (zd == null)
        { // Zug völlig neue, noch keine Details da
            zd = new ZugDetailFahrplan(details);
            zuglist.put(zid, zd);
            this.request_zugfahrplan(zid); // Fahrplan abfragen
        }
        else
        {
            zd.update(details);
            this.request_zugfahrplan(zid);
        }
        repaint();
    }
 
    /**
     * Antwort vom <zugfahrplan zid>
     * @param zid
     * @param plan
     */
    @Override
    protected void response_zugfahrplan(int zid, LinkedList<ZugFahrplanZeile> plan)
    {
        System.out.println("Fahrplan für " + zid);
        ZugDetailFahrplan zd = zuglist.get(zid);
        if (zd != null)
        {
            zd.plan = plan; // Fahrplan so speichern
        }
        else
        {
            System.out.println("Fehler: Für " + zid + " fehlen die Details!");
        }
    }
 
    /**
     * @return name des Stellwerks
     */
    public String getName()
    {
        return name;
    }
 
    /**
     * enthält jeweils einen Zug für die Abfahrtwand
     */
    public class Abfahrten implements Comparable
    {
 
        static private final String SPACER = "     ";
        public final String name;       /// Zugname
        public final String gleis;      /// Gleisname (ggf. nach Gleisänderung)
        public final String ab;         /// Abfahrtzeit (formatiert HH:MM)
        public final String nach;       /// Zugziel (Ausfahrt)
        public final String infotext;   /// ein Info-Text mit Verspätung, Gleisänderung, etc.
        private final long abLong;      /// Abfahrtzeit in Millis, zum Sortieren
 
        Abfahrten(ZugDetailFahrplan z, ZugFahrplanZeile f)
        {
            String t = "";
            if (z.verspaetung > 5)
            {
                t += "ca. " + ((z.verspaetung / 5) * 5) + " Min später" + SPACER;
            }
 
            name = z.name;
            gleis = cleverGleis(f.gleis);
            if (!f.plan.equals(f.gleis))
            {
                t += "Gleisänderung von Gleis " + cleverGleis(f.plan) + SPACER;
            }
            else if (z.gleis != null && z.plangleis != null && f.plan.equals(z.plangleis) && !z.plangleis.equals(z.gleis))
            {
                t += "Gleisänderung von Gleis " + cleverGleis(z.plangleis) + SPACER;
            }
            abLong = f.ab;
            ab = f.getFormattedAb();
            nach = z.nach;
            if (f.flags.hasFlag('R'))
            {
                t += "Zug ändert Fahrtrichtung" + SPACER;
            }
            if (f.flags.hasFlag('F'))
            {
                int fzid = f.flags.dataOfFlag('F');
                ZugDetailFahrplan fz = zuglist.get(fzid);
                if (fz == null || fz.nach.isEmpty())
                {
                    t += "Zug wird geteilt" + SPACER;
                }
                else
                {
                    t += "Zug wird geteilt, vorderer Zugteil nach " + nach + ", hinterer Zugteil als " + fz.name + " nach " + fz.nach + SPACER;
                }
            }
            infotext = t;
        }
 
        /**
         * führende Buchstaben im Gleisnamen entfernen
         * @param g
         * @return
         */
        private String cleverGleis(String g)
        {
            StringBuilder ret = new StringBuilder(g);
            while (ret.length() > 0 && !Character.isDigit(ret.charAt(0)))
            {
                ret.deleteCharAt(0);
            }
            return ret.toString();
        }
 
        @Override
        public int compareTo(Object o)
        {
            Abfahrten oo = (Abfahrten) o;
            int r = (int) (abLong - oo.abLong);
            if (r == 0)
            {
                r = name.compareToIgnoreCase(oo.name);
            }
            if (r == 0)
            {
                r = gleis.compareToIgnoreCase(oo.gleis);
            }
            return r;
        }
    }
 
    /**
     * liefert eine sortierte Liste der noch ausstehenden Abfahrten für die
     * ausgewählten Bahnsteige
     *
     * Ablauf ist der simpel: Züge, deren Ist- oder Soll-Gleis im Menü ausgewählt ist,
     * werden in die Liste aufgenommen. Die Sortierung macht diese für uns automatisch.
     * Die Wand gibt später alle Züge der Reihe nach aus, bis die Wand voll ist.
     * Weggelassen werden endende Züge (E-Flag und K-Flag), denn die haben keine Abfahrt.
     * @return
     */
    public TreeSet<Abfahrten> getAbfahrten()
    {
        TreeSet<Abfahrten> ret = new TreeSet<connection.Abfahrten>();
 
        for (ZugDetailFahrplan z : zuglist.values())
        {
            if (z.gleis != null)
            {
                try
                {
                    System.out.println("Zug: " + z.name + " von " + z.von + " ab " + z.getFormattedAb());
                    for (ZugFahrplanZeile g : z.plan)
                    {
                        if (!g.flags.hasFlag('E') && !g.flags.hasFlag('K') && !g.flags.hasFlag('D'))
                        {
                            JCheckBoxMenuItem cb = bahnsteige.get(g.gleis);
                            boolean add = (cb != null && cb.isSelected());
                            cb = bahnsteige.get(g.plan);
                            add |= (cb != null && cb.isSelected());
                            if (add)
                            {
                                System.out.println("zeigen");
                                ret.add(new Abfahrten(z, g));
                            }
                        }
                        else
                        {
                            System.out.println("Flag");
                        }
                    }
                }
                catch (NullPointerException ex)
                {
                    System.out.println("Unvollständig: " + z.name + "(" + z.zid + ")");
                }
            }
        }
        return ret;
    }
}