stsplugin
Stellwerksim Plugin-Client
Dieses Modul stellt eine PluginClient-Klasse zur Verfügung, die die Kommunikation mit dem Simulator übernimmt. Der Client speichert auch alle Anlagen- und Fahrplandaten zwischen und verarbeitet Ereignisse. Der PluginClient übersetzt Anfragen in xml und sendet sie an den Simulator. Die xml-Antworten übersetzt er in Python-Objekte.
Asynchrone Kommunikation
Die Kommunikation verläuft asynchron und ist nach der trio-Bibliothek modelliert. Es gibt einen Socket-Stream für die xml-Kommunikation mit dem Simulator. Die request-Methoden senden Anfragen via diesen Stream. Antworten werden innerhalb der Receiver-Methode, die in einem eigenen trio-Task gestartet wird, verarbeitet und als Python-Objekte in die Antworten- oder Ereignis-Queues gestellt. Die request-Methoden holen die Antworten dort ab. Für Ereignisse kann das Hauptprogramm einen separaten Task starten und die Queue auslesen.
Vorsicht ist bei der Verwendung von parallelen Tasks geboten, damit sich zwei Serveranfragen nicht überschneiden können. Am besten werden alle Anfragen im gleichen trio-Task gestellt. In einem separaten Tawenn der sk wird die Ereignis-Queue abgefragt.
Beispiele für die Implementation zeigen das Testprogramm unten, oder weitere im Paket enthaltene Programme.
Logging
stsplugin nutzt einen eigenen Logger mit dem namen "stsplugin" aus dem logging-Modul der Standardbibliothek.
Tip
Auf Stufe DEBUG wird die gesamte Kommunikation mit dem Simulator ausgegeben!
Wenn dies nicht gewünscht wird, der Rest des Programms aber auf DEBUG bleiben soll,
kann die Stufe dieses Moduls individuell angepasst werden durch
logging.getLogger('stskit.interface.stsplugin').setLevel(logging.WARNING).
Classes:
-
PluginClient–PluginClient - der Kern der Plugin-Schnittstelle
-
TaskDone–Task erfolgreich erledigt
Functions:
-
ausfahrt_sortierschluessel–sortierschlüssel-funktion für zugeinfahrten erzeugen.
-
einfahrt_sortierschluessel–Sortierschlüssel-Funktion für Zugeinfahrten erzeugen.
-
test–Testprogramm
-
zugsortierschluessel–Sortierschlüssel-Funktion für Züge an einem Gleis erzeugen.
PluginClient
PluginClient - der Kern der Plugin-Schnittstelle
Attributes:
-
name(str) –Name des Plugins. Wird beim Start im Sim angezeigt.
-
autor(str) –Autor des Plugins. Wird beim Start im Sim angezeigt.
-
version(str) –Version des Plugins. Wird beim Start im Sim angezeigt.
-
text(str) –Beschreibung des Plugins. Wird beim Start im Sim angezeigt.
-
anlageninfo(AnlagenInfo | None) –AnlagenInfo vom Simulator. Wird durch request_anlageninfo abgefragt.
-
bahnsteigliste(dict[str, BahnsteigInfo]) –Dict von BahnsteigInfo-Objekten nach Gleisnamen. Wird durch request_bahnsteigliste abgefragt.
-
gleisabgleich(dict[str, str]) –Dict zur Korrektur von falsch geschriebenen Gleisnamen. Im Stellwerk Brennero wurden Züge mit gross geschriebenen Gleisnamen beobachtet, obwohl die Gleisnamen im Stellwerk Gemischtschreibung verwenden. In der aktuellen Version ordnet der Dict den kleingeschriebenen Gleisnamen die Originalnamen aus der Bahnsteigliste zu.
-
zugliste(dict[int, ZugDetails]) –List von ZugDetails-Objekten mit den Informationen zu den Zügen. Wird durch request_zugliste und request_zugdetails abgefragt.
-
wege(dict[int | str, Knoten]) –Dict von Knoten-objekten repräsentiert den Wege-Graph. Der Dict ist nach Knoten.key aufgeschlüsselt. Dies ist für die meisten Element die enr. Für Bahnsteige und Haltepunkte ist es der Name. Vorsicht: Bahnsteige und Einfahrten/Ausfahrten können den gleichen Namen haben.
-
wege_nach_enr(dict[int, Knoten]) –Wege-Graph nach enr-Nummern aufgeschlüsselt. Der Dict enthält die Knoten-Objekte, die eine enr-Nummer deklariert haben: Signale, Weichen, Einfahrten und Ausfahrten.
-
wege_nach_namen(dict[str, set[Knoten]]) –Wege-Graph nach knoten-Namen aufgeschlüsselt. Der Dict enthält die Knoten-Objekte, die einen Namen deklariert haben. Weil der Name nicht eindeutig ist (Anschlüsse und Bahnsteige haben z.B. in einigen Stellwerken den gleichen Namen), sind die Werte des Dicts Sets.
-
wege_nach_typ(dict[int, set[Knoten]]) –Wege-Graph nach Typnummer aufgeschlüsselt. Der Dict enthält Sets von Knoten-objekten.
-
wege_verbindungen(set[tuple[int | str, int | str]]) –Set von Zweiertuples Knoten.key mit den Verbindungen zwischen Knoten. Kann als Kantenliste für den Aufbau von Graphen verwendet werden. Achtung: Es kann Kanten geben, deren Endpunkt in
wegekeinen Knoten haben, s.fehlende_wege_knoten. -
fehlende_wege_knoten(set[int | str]) –Set von Wegeknoten, die in einer
wege_verbindungenvorkommen, aber inwegenicht erfasst sind. Gewisse Rangiersignale aus Einfahrten (Beispiele S210, S289, S308, S218 in Bozen) haben keinen Knoten, d.h. wir können ihren Typ und Namen nicht ermitteln. Diese Signale fehlen dann auch im Signalgraph. Aktuell sollte dies aber kein Problem darstellen. -
fehlende_wege_kanten(set[tuple[int | str, int | str]]) –Set von Verbindungen analog
wege_verbindungen, von denen mindestens ein Endpunkt inwegenicht erfasst ist. Der entsprechende Endpunkt ist infehlende_wege_knoteneingetragen. Die nicht aufgelösten Kanten sind trotzdem inwege_verbindungenenthalten.
Example
Siehe Beispielcode am Ende des Moduls (test-Funktion).
Methods:
-
calc_simzeit–Simulatorzeit ohne Serverabfrage abschätzen.
-
gleis_abgleichen–Gleisnamen mit Bahnsteigliste abgleichen und ev. korrigieren
-
receiver–Empfangsschleife: Antworten empfangen und verteilen
-
register–Klient beim Simulator registrieren.
-
request_anlageninfo–Anlageninfo anfordern.
-
request_bahnsteigliste–Bahnsteigliste anfordern.
-
request_ereignis–Ereignismeldung anfordern
-
request_simzeit–Simulatorzeit anfragen.
-
request_wege–Wege-Graph anfragen
-
request_zug–Einzelnen Zug und Fahrplan anfragen.
-
request_zugdetails–ZugDetails eines, mehrerer oder aller Züge anfragen.
-
request_zugdetails_einzeln–ZugDetails eines einzelnen Zuges anfragen.
-
request_zugfahrplan–Fahrplan eines, mehrerer oder aller Züge anfragen.
-
request_zugfahrplan_einzeln–Fahrplan eines Zuges anfragen.
-
request_zugliste–Zugliste anfragen.
-
resolve_zugflags–Folgezüge aus den Zugflags auflösen.
-
update_bahnsteig_zuege–Züge in Bahnsteigliste eintragen.
-
update_wege_zuege–Züge in Wegelisten eintragen.
Source code in stskit/plugin/stsplugin.py
antwort_channel_in
property
Eingang asynchrone Warteschlange für Antworten vom Simulator
Antworten vom Simulator werden während des Parsens als Element-Objekte an diese Warteschlange übergeben.
antwort_channel_out
property
Ausgang asynchrone Warteschlange für Antworten vom Simulator
Antworten vom Simulator können asynchron als Element-Objekte aus dieser Warteschlange entnommen und verarbeitet werden.
ereignis_channel_in
property
ereignis_channel_in: SendChannel[Ereignis]
Eingang asynchrone Warteschlange für Ereignismeldungen vom Simulator
Ereignismeldungen vom Simulator werden während des Parsens als Ereignis-Objekte an diese Warteschlange übergeben.
ereignis_channel_out
property
ereignis_channel_out: ReceiveChannel[Ereignis]
Ausgang asynchrone Warteschlange für Ereignismeldungen vom Simulator
Ereignismeldungen vom Simulator können asynchron als Ereignis-Objekte aus dieser Warteschlange entnommen und verarbeitet werden.
calc_simzeit
calc_simzeit() -> datetime
Simulatorzeit ohne Serverabfrage abschätzen.
Der time_offset muss vorher einmal mittels request_simzeit kalibriert worden sein.
Der Rückgabewert enthält das aktuelle (Client-)Datum.
Das ist nötig, damit mit der Uhrzeit gerechnet werden kann.
Da der Simulator kein Datum kennt, sollten die Datumsfelder nach der Rechnung nicht beachtet werden.
Der Fahrplan (in FahrplanZeile) enthält lediglich datetime.time-Objekte.
Ein datetime.time-Objekt kann einfach über die time-Methode extrahiert werden.
Returns:
-
datetime–Extrapolierte Simulatorzeit
Source code in stskit/plugin/stsplugin.py
gleis_abgleichen
gleis_abgleichen(gleis_name: str) -> str
Gleisnamen mit Bahnsteigliste abgleichen und ev. korrigieren
Die Methode versucht, Gleisnamen, die in der Bahnsteigliste nicht enthalten sind, mittels des gleisabgleich-Dict aufzulösen, um Gross/Kleinschreibung und ggf. andere Schreibfehler im Zugfahrplan zu korrigieren.
Parameters:
-
(gleis_namestr) –Original-Gleisname
Returns:
-
str–abgeglichener Name
Source code in stskit/plugin/stsplugin.py
receiver
async
Empfangsschleife: Antworten empfangen und verteilen
Alle Antworten ausser Ereignisse werden in untangle.Element-Objekte gepackt und an die Antworten-Queue übergeben. Ereignisse werden als stskit.model.Ereignis-Objekte an die Ereignisse-Queue übergeben.
Diese Coroutine muss explizit in einer trio.nursery gestartet werden und läuft, bis die Verbindung unterbrochen wird.
Source code in stskit/plugin/stsplugin.py
register
async
Klient beim Simulator registrieren.
Diese Funktion muss als erste nach dem Verbinden aufgerufen werden, da sie auch die Statusantwort nach der Verbindungsaufnahme auswertet.
Source code in stskit/plugin/stsplugin.py
request_anlageninfo
async
Anlageninfo anfordern.
Die Antwort wird im anlageninfo-Attribut gespeichert.
Source code in stskit/plugin/stsplugin.py
request_bahnsteigliste
async
Bahnsteigliste anfordern.
Die Liste wird im Attribut bahnsteigliste gespeichert.
Die Methode löst auch die nachbarn der Bahnsteige auf.
Source code in stskit/plugin/stsplugin.py
request_ereignis
async
Ereignismeldung anfordern
Nach Nummernwechsel muss man Ereignismeldungen neu anfordern. Ausser für "Einfahrt" schicken wir daher Anforderungen nur, wenn der Zug sichtbar ist.
Anforderungen werden in registrierte_ereignisse notiert,
damit sie nicht wiederholt gesendet werden.
Parameters:
-
(artstr) –art des ereignisses, cf. model.Ereignis.arten
-
(zidsIterable[int]) –menge oder sequenz von zug-id-nummern
Source code in stskit/plugin/stsplugin.py
request_simzeit
async
request_simzeit() -> datetime
Simulatorzeit anfragen.
Die Funktion fragt die aktuelle Simulatorzeit an und liefert sie in einem datetime.time-Objekt.
Basierend auf der Antwort setzt sie ausserdem client_datetime, server_datetime und time_offset.
Diese Attribute können benutzt werden, um die Simulatorzeit zu berechnen (calc_simzeit-Funktion),
ohne dass eine erneute Anfrage geschickt werden muss.
Info
client_datetime und server_datetime enthalten das aktuelle Datum.
Das ist nötig, um den time_offset als timedelta zu berechnen.
Da der Simulator kein Datum kennt, sollten die Datumsfelder nicht beachtet werden.
Die datetime.datetime.time-Methode ist ein schneller Weg, ein datetime.time-Objekt zu erhalten.
Returns:
-
datetime–Aktuelle Simulatorzeit
Source code in stskit/plugin/stsplugin.py
request_wege
async
Wege-Graph anfragen
Der Wege-Graph enthält die Elemente des Gleisbilds und ihre Verbindungen. Im PluginClient wird er als Dict von Knoten-Objekten dargestellt, die über ihre Nachbarn-attribute verlinkt sind. Für eine Darstellung mittels networkx-Graphen, siehe stsgraph.GraphClient.
Da der Simulator für die Elemente zwei verschiedene Schlüssel (enr und name) verwendet, ist der Schlüssel des Wege-Dict zweiteilig und enthält den Elementtyp und - je nach Typ - entweder die enr oder den Namen.
Die Methode aktualisiert folgende Attribute: wege, wege_nach_enr, wege_nach_namen, wege_nach_typ, wege_verbindungen, fehlende_wege_knoten, fehlende_wege_kanten.
Teilweise fehlen wichtige Gleisverbindungen in dem Graphen, z.B. von Anschlüssen ans Gleisnetz.
Rangiersignale aus Einfahrten (Beispiele S210, S289, S308, S218 in Bozen) haben keinen Knoten, werden aber in Kanten referenziert. Wir geben in diesem Fall eine Info-Meldung 'Nicht auflösbare Elementreferenz' ins Log und schreiben die Verbindung und Referenz in die fehlende_wege_knoten- und fehlende_wege_kanten-Listen.
Source code in stskit/plugin/stsplugin.py
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 | |
request_zug
async
request_zug(zid: int) -> ZugDetails | None
Einzelnen Zug und Fahrplan anfragen.
Der Zug wird in die Zugliste eingetragen bzw. aktualisiert und als ZugDetails-Objekt zurückgegeben.
Parameters:
-
(zidint) –einzelne zug-id
Returns:
-
ZugDetails | None–ZugDetailsinkl. Fahrplan. -
ZugDetails | None–None, wenn der Zug nicht verzeichnet ist.
Source code in stskit/plugin/stsplugin.py
request_zugdetails
async
ZugDetails eines, mehrerer oder aller Züge anfragen.
Wenn ein ZugDetails-Objekt mit der zid bereits in der Zugliste angelegt ist, wird es aktualisiert, andernfalls neu angelegt. Wenn ein Fehler auftritt (weil z.B. der Zug nicht mehr im Stellwerk ist), wird der Zug aus der Zugliste gelöscht.
Parameters:
-
(zidint | Iterable[int] | None, default:None) –Einzelne Zug-ID, Iterable von Zug-IDs, oder None (alle in der Zugliste).
Source code in stskit/plugin/stsplugin.py
request_zugdetails_einzeln
async
ZugDetails eines einzelnen Zuges anfragen.
Wenn ein ZugDetails-Objekt mit der angegebenen zid bereits in der Zugliste angelegt ist, wird es aktualisiert, andernfalls neu angelegt. Wenn ein Fehler auftritt (weil z.B. der Zug nicht mehr im Stellwerk ist), wird der Zug aus der Zugliste gelöscht.
Parameters:
-
(zidint) –einzelne zug-id.
Returns:
-
bool–True (Erfolg) oder False (Fehler, Zug entfernt)
Source code in stskit/plugin/stsplugin.py
request_zugfahrplan
async
Fahrplan eines, mehrerer oder aller Züge anfragen.
Das ZugDetails-Objekt muss in der Zugliste bereits existieren.
Abgefahrene Wegpunkte sind im Fahrplan nicht mehr vorhanden.
Parameters:
-
(zidint | Iterable[int] | None, default:None) –einzelne zug-id, iterable von zug-ids, oder None (alle in der liste).
Source code in stskit/plugin/stsplugin.py
request_zugfahrplan_einzeln
async
Fahrplan eines Zuges anfragen.
Das ZugDetails-Objekt muss in der Zugliste bereits existieren. Der Fahrplan wird bei der ersten Anfrage komplett übernommen. Bei den folgenden Anfragen, wird nur die aktuelle Gleisänderung übernommen, die anderen Attribute bleiben unverändert (s. Bemerkung unten).
Die Methode aktualisiert auch den ziel_index des Zuges.
Abgefahrene Wegpunkte (ausser dem letzten) sind in der Antwort vom Simulator nicht mehr vorhanden. Wir behalten jedoch den Fahrplan im ZugDetails bei, so wie er bei der ersten Aufruf zurückgegeben wurde. Lediglich die Gleisänderung wird aktualisiert.
Der Fahrplan vom Simulator enthält den letzten Wegpunkt auch, nachdem er passiert wurde. Der Eintrag kann dann aber fehlerhafte Werte enthalten (Dezember 2024). Z.B. kann das Flags-Attribut Fall leer sein, wenn es vorher eine Durchfahrt angezeigt hat.
Ersatzloks haben im XML keine Plangleis- und Gleisangabe. Wir übernehmen beim ersten Auftreten den ersten Fahrplaneintrag.
Parameters:
-
(zidint) –einzelne zug-id, iterable von zug-ids, oder None (alle in der liste).
Returns:
-
bool–True (Erfolg) oder False (Fehler)
Source code in stskit/plugin/stsplugin.py
638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 | |
request_zugliste
async
Zugliste anfragen.
Die Zugliste wird angefragt und aktualisiert.
Die vom Simulator gelieferte Zugliste enthält nicht alle Folgezüge.
Ausgefahrene Züge werden von der Liste entfernt. Für ersetzte Züge wird ein ersatz-Ereignis erzeugt.
Die Zugobjekte sind nach dieser Abfrage schon ziemlich komplett. Es fehlen die aktuelle Verspätung (request_zugdetails) und Gleisänderungen im Fahrplan (request_zugfahrplan).
Die Objektinstanzen werden bei Aktualisierung beibehalten.
Ersatzloks haben eine negative ID.
Source code in stskit/plugin/stsplugin.py
resolve_zugflags
async
Folgezüge aus den Zugflags auflösen.
Da request_zugliste die Folgezüge (Ersatz-, Flügel- und Kuppelzüge) nicht automatisch erhält,
lesen wir diese aus den Zugflags aus und fragen ihre Details und Fahrpläne explizit an.
Die Funktion arbeitet iterativ, bis alle Folgezüge aufgelöst sind.
Die Züge werden in die Zugliste eingetragen und im Stammzug referenziert.
Info
zids sind nicht geordnet. Ersatzzüge können eine tiefere zid als der Stammzug haben.
Parameters:
-
(zidint | Iterable[int] | None, default:None) –Einzelne Zug-ID, Iterable von Zug-IDs, oder None (alle in der Liste).
Source code in stskit/plugin/stsplugin.py
update_bahnsteig_zuege
Züge in Bahnsteigliste eintragen.
Im zuege-attribut der Bahnsteige werden die an den Bahnsteig disponierten Züge aufgelistet.
zuege ist ein Dictionary und bildet zid auf ZugDetails ab.
Die ZugDetails sind weak References.
Source code in stskit/plugin/stsplugin.py
update_wege_zuege
Züge in Wegelisten eintragen.
Im Züge-Attribut der Wege und Knoten (Einfahrten, Ausfahrten, Haltepunkte) werden die fahrplanmässig daran vorbei kommenden Züge aufgelistet.
Source code in stskit/plugin/stsplugin.py
TaskDone
flowchart TD
stskit.plugin.stsplugin.TaskDone[TaskDone]
click stskit.plugin.stsplugin.TaskDone href "" "stskit.plugin.stsplugin.TaskDone"
Task erfolgreich erledigt
Die exception signalisiert, dass die Aufgaben erfolgreich abgearbeitet worden sind.
Die Exception kann vom Hauptprogramm ausgelöst werden, um einen trio-nursery-Kontext zu verlassen, der ansonsten unbestimmt auf andere Tasks warten würde. Die Exception muss ausserhalb des Kontexts abgefangen werden.
ausfahrt_sortierschluessel
sortierschlüssel-funktion für zugeinfahrten erzeugen.
der sortierschlüssel ist die ankunfts- oder abfahrtszeit des letzten fahrplanziels oder der default-wert, wenn der fahrplan leer ist oder die zeitangabe fehlt.
Parameters:
-
(attrstr) –Name des Zeitattributs, entweder
anoderab. -
(defaulttime) –Defaultwert, falls das Attribut fehlt.
Returns:
-
Callable–Sortierschlüssel-Funktion für sorted().
Source code in stskit/plugin/stsplugin.py
einfahrt_sortierschluessel
Sortierschlüssel-Funktion für Zugeinfahrten erzeugen.
Der Sortierschlüssel ist die Ankunfts- oder Abfahrtszeit des ersten Fahrplanziels oder der Defaultwert, wenn der Fahrplan leer ist oder die Zeitangabe fehlt.
Parameters:
-
(attrstr) –Name des Zeitattributs, entweder
anoderab. -
(defaulttime) –Defaultwert, falls das Attribut fehlt.
Returns:
-
Callable–Sortierschlüssel-Funktion für sorted().
Source code in stskit/plugin/stsplugin.py
test
async
test(*args, **kwargs) -> PluginClient
Testprogramm
Das Testprogramm fragt alle Daten einmalig vom Simulator ab und gibt sie an stdout aus.
Der PluginClient bleibt bestehen, damit weitere Details aus den statischen Attributen ausgelesen werden können. Die Kommunikation mit dem Simulator wird jedoch geschlossen.
Returns:
-
PluginClient–PluginClient-Instanz
Source code in stskit/plugin/stsplugin.py
zugsortierschluessel
Sortierschlüssel-Funktion für Züge an einem Gleis erzeugen.
Der Sortierschlüssel ist die Ankunfts- oder Abfahrtszeit am angegebenen Gleis im Fahrplan oder der default-Wert, wenn die Fahrplanzeile oder Zeitangabe fehlt.
Parameters:
-
(gleisstr) –Name des Gleises oder Bahnsteigs.
-
(attrstr) –Name des Zeitattributs, entweder
anoderab. -
(defaulttime) –Defaultwert, falls das Attribut fehlt.
Returns:
-
Callable–sortierschlüssel-funktion für sorted().