Kleiner Zwischenbericht:
Die Kommunikationstechnik steht immer noch im Zentrum der momentanen Arbeit. Also ganz tief unten. Direkte Stellwerksfunktionalität wird man da vielleicht noch gar nicht erkennnen, aber die Kommunikationsplattform ist essentiell fürs Ganze.
Wie ja schon mehrfach geschrieben, soll es möglich sein, Stellwerke zur Laufzeit der Simulation an- und abzumelden. Auch nicht ordnungsgemäße Abmeldung (Verbindungsabbruch) soll abgefangen werden. Für die Kommunikation ist besonders die Anmeldung spannend, müssen doch alle ein Stellwerk betreffenden Zustände an dieses übermittelt werden. Die verwendete Technik dazu ist eine Kombination aus synchron und asynchron. Die Schnittstellen sind entsprechend ausgelegt, nach außen hin werden Methoden (synchron) und Ereignisse (asynchron) bereit gestellt.
Der innere Ablauf ist multi-threaded. Dies ist zum einen durch .Net-Remoting intern vorgegeben, aber auch sonst sinnvoll, will man dich weitgehende Entkopplung der einzelnen Kommunikationsvorgänge, um gegenseitige Blockade zu verhindern.
Ein Stellwerk selbst wird zum Benutzer hin aber im wesentlichen single-threaded sein, das ist technisch bedingt. Fensteroberflächen arbeiten nach einem ereignisgesteuerten Paradigma (auch X-Windows), und alle Ereignisse, ab Maus, Tastatur oder oder eine Gleiskontaktmeldung, müssen in denselben Thread eingeschleift werden. Softwaretheoretisch läuft das darauf hinaus, eine Methode eines Objektes unter einem anderen Thread als dem eigenen auszuführen. Dies geht prinzipiell sowohl synchron als auch asynchron. Es gibt verschiedene Begriffe dafür, "Active Object" (Doug Schmidt) oder "Future" (aus der Java-Welt). Die Bibliotheken der Fensteroberflächen haben solche Techniken implementiert. Die moderne Softwarewelt, die alles Verhalten als Schnittstellen ausbildet, zeigt uns die Systematik in nachvollziehbarer Form. Das Einschleifen in einem fremden Thread heißt bei .Net: ISynchronizeInvoke. Die gleiche Technik wird höchstwahrscheinlich in der COM-Ausprägung von Delphi realisiert sein, muss ich demnächst noch mal verifizieren.
Hat man diesen Synchronisationsmechanismus einmal verstanden, kann man ihn baukastenmäßig auch anderes zusammenstellen. Das ist für die Testerei wichtig. Die relative Komplexität der Kommunikation einerseits, aber ihre klare Systematik andererseits schreit nach entsprechend systematischen Testverfahren: Unit-Tests. Entsprechende Testsoftware, ursprünglich für Java entwickelt (JUnit), gibt es heute für fast jede gängige Programmiersprache (u.a. auch für C++: CppUnit oder für Delphi: DUnit). Für .Net heißt sie NUnit. Besonders hübsch bei NUnit ist die Nutzung der Klassenattribute von .Net. Damit kann man Klassen bestimmte Aspekte mitgeben, z.B. Vorgaben zur Serialsierung oder zur Darstellung in Eigenschaftsdialogen. Oder eben zum Testverhalten. Gewisse Kompromisse muss man für systematisches Testen leider dennoch machen. Graphische Benutzeroberflächen sollten hierfür nämlich außen vor bleiben. Aber ein gutes Design trennt bekanntlich Form und Funktion. Bleibt nur die Sache mit dem Ereignis-Thread der Windows-Oberfläche. Auch dass lässt sich lösen, wenn man ISynchronizeInvoke mit einer eigenen Klasse implementiert.
Warum muss das eigentlich so "kompliziert" sein? Dazu vielleicht als Beispiel nochmal die grundsätzlichen Kommuikationswege, so wie sich für unser Stellwerkskonzept darstellen (der Leser wird sich an die Analyse zu beginn des Projekts erinnern):
- Weichen und Signale werden vom Stellwerk gestellt, von Zusi rückgemeldet. Zusi selbst stellt auch Weichen und Signale, aber nur dann wenn das Stellwerk nicht angemeldet ist. Signalrückmeldungen können auch an ein Nachbarstellwerk gehen, Stichwort "Signalmelder". Der Stellvorgang ist synchron, SIgnalrückmeldungen an ein anderes Stellwerk aber asynchron.
- Gleiskontakte werden von Zusi an ein Stellwerk gesendet, sofern angemeldet. Asynchron. Gleiches Verhalten für Gleisfreimeldeabschnitte, auch wenn die im mech. Stellwerk nicht vorkommen. Hier kann es aber mehr als einen Abonnenten geben.
- Blockfeldmeldungen werden zwischen je genau zwei Stellwerken ausgetauscht (Bahnhofsblock eingeschlossen). Die Stellwerke können auch durch Zusi emuliert sein.
- Zugmeldungen und sonstige bei Vorbild fernmündliche oder -schriftliche Meldungen haben einen im Prinzip transienten Charakter, d.h. sie beinhalten keinen wirklichen Zustand. Neben bilateraler Kommunikation gibt es auch Rundsprüche, "Chat" in moderner Lingua.
- Fahrstraßen (Zugstraßen) haben für die Stellwerkskommunikation etwas ambivalentes. Fahrstraßen sind Funktionselemente des Stellwerks und unterliegen dessen alleiniger Verantwortung. Sie haben keine Entsprechung in Außenanlagen (Zusi). Jedoch ist Zusi im Stellwerkemulationsbetrieb (Normalbetrieb bei Zusi) seinerseits für die Fahrstraßen verantwortlich. Beim Anmelden eines Stellwerks und der Übernahme des Betriebs von Zusi muss das Stellwerk über den aktuellen Zustand der Fahrstraßen informiert werden. Denn das Stellwerk ist nicht notwendigerweise in Grundstellung, wenn die Anmeldung erfolgt. Eine Abmeldung des Stellwerks kann jederzeit und abrupt erfolgen, ohne die Chance, den Zustand der Fahrstraßen wieder an Zusi zurückzumelden. Deshalb wird das Zusi-Stellwerk Fahrstraßenzustandsänderungen laufend an Zusi melden. Eine logische Funktion ist damit aber während der Betriebszeit des Stellwerks nicht verbunden.
Mehrfach war von synchronen und asynchronen Meldungen die Rede. So kann man den Zustand eines Gleiskontaktes synchron abfragen, aber fast gleichzeitig mag sich dessen Zustand ändern, und eine asynchrone Meldung ausgelöst werden. Die Wege zum Stellwerk sind für synchrone Rückmeldung und asynchrone Ereignismeldung unterschiedlich. Die asynchrone Meldung kann verzögert werden. Wie aktuell ist sie noch, wenn sie beim Stellwerk ankommt? Die Funktionslemente des Stellwerks habe häufig binäre Zustände: gestellt oder nicht gestellt, geblockt oder nicht geblockt. Da macht es schon einen wesentlichen Unterschied, ob der gerade empfangene Zustand auch der letzte ist. Wie stellt man das fest? Zeitstempel scheinen eine Möglichkeit, aber ist ihre Granularität ausreichend? Die Auflösung beträgt "nur" 10 ms. Zudem ist die Zeit bei Zusi ja eigentlich Schall und Rauch, denn sie unterliegt der fast beliebigen Manipulation durch Fahrplan und Benutzer. Die Zeit kann daher nur Attribut sein, nicht Schlüssel. Die Lösung heißt Sequenznummer. Jeder Meldevorgang an der Zusi-Schnittstelle, gleich ob synchron oder asynchron, wird je Element mit einer fortlaufenden und eindeutigen Sequenznummer versehen. Diese Schnittstelle gibt es nur einmal, und hier kann die Ordnung garantiert werden. Trifft also eine Meldung mit einer niedrigeren Sequenznummer beim Stellwerk ein, als bereits vorhanden, so kann man diese Meldung als veraltet verwerfen.
Soweit mein Zwischenbericht, jetzt heißt es leider wieder Unterbrechung zugunsten aktueller AR- und GF-Problemchen.
PS: Als .Net 2.0 auf den Markt kam und Generics(Templates) einführte, hatte ich gehofft, die von mir innig geliebten Collection-Klassen würden gründlich verbessert und systematisiert. Das erfolgte auch zum Teil, der Umfang blieb aber relativ bescheiden. Es gibt jedoch Abhilfe, die "C5"-Bibliothek, von Kopenhagener Wissenschaftlern, sogar ohne Lizenzrestriktionen: http://www.itu.dk/research/c5/" target="_blank Damit fühlt man sich als langjähriger C++-Anwender der STL auch in .Net zu Hause. Hier findet man Lösungen, wenn es komplizierter wird. Die Stellwerkstechnik allerdings kommt bisher noch mit den einfachen Collection-Klassen des .Net-Frameworks aus.