Kochbuch: Fahrpläne im Script
Da die Prozeduren und Funktionen, die rund um das Thema "Fahrpläne und Disposition im Script-System" vorhanden sind, ziemlich umfangreich und speziell sind, präsentieren wir hier einige Beispiele, wie diese anzuwenden sind.
Allgemeines
Bei den folgenden Prozeduren und Funktionen wird im Sinne der Einfachheit keine Rücksicht auf besondere Effizienz genommen, da in erster Linie am Beispiel erklärt werden soll, wie die Funktionen arbeiten. Es wird daher am Ende der Beispiele jeweils allgemein vorgeschlagen, wie man den Ablauf auch effizienter gestalten kann.
Vorarbeiten
Für diverse Funktionen wird der Name des RBL-Servers gebraucht, auf den sich das RBL-Bordgerät anmelden soll. Diese Information wird in der Basis-FIS-Datei hinterlegt. Da sie sich fortan nicht mehr verändert, ist es sinnvoll, diese in eine Variable abzulegen. Hierbei sollte es sich um keine "PUBLIC_VARS"-Variable handeln, sondern um eine, die lediglich außerhalb der Prozeduren deklariert wird.
Geschrieben werden soll die Variable nicht in der Initialize-Prozedur, sondern einmalig beim ersten Durchlauf von SimStep:
var NotFirstRun: boolean; RBL_Server: string; ... procedure Initialize; begin NotFirstRun := false; ... end; procedure SimStep; begin if not NotFirstRun then begin RBL_Server := PIS_GetITCSServer(Self); ... // weitere Dinge, die nur einmalig aus SimStep heraus aufgerufen werden sollen NotFirstRun := true; end; ... end;
Routen (keine Zeiten)
Wo befindet sich das Fahrzeug auf einer bestimmten Route?
Angenommen: Im RBL-Bordgerät wurden vom User Linie und Route eingegeben und es soll nun ermittelt werden, ob und wo sich das Fahrzeug auf der Strecke befindet. Zurück gegeben wird die sogenannte "Sektion". Hierbei wird bei der Starthaltestelle mit Nummer 0 begonnen zu zählen und dann jeweils abwechselnd der Abschnitt zwischen den Stationen und die Stationen selbst gezählt:
0: an der Abfahrtsstation 1: zwischen der ersten und zweiten Station 2: an der zweiten Station 3: zwischen der zweiten und dritten Station
function WoAufDerRoute(linie: integer; route: integer): integer; var linieStr: string; routeStr: string; wayIndex: integer; // interner Index von Route/Wege begin // Die Prozeduren arbeiten zwecks der Flexibilität mit Strings. // Daher müssen die eingegebenen Daten zunächst in Strings umgewandelt werden: linieStr := IntToStr(linie); routeStr := IntToStr(route); wayIndex := TimetableGetWayIndex(Self, RBL_Server, linieStr, routeStr); // Falls der Index negativ ist, dann ist die eingegebene Route nicht vorhanden // Dann soll einfach -1 zurück gegeben werden: if wayIndex < 0 then begin result := -1; end else begin result := TimetableAtSectionOfWay(Self, wayIndex); end; end;
Effizienter ist es, wenn der wayIndex nur neu ermittelt wird, wenn sich linieStr und/oder routeStr ändern bzw. wenn eine Eingabe erfolgt, die potentiell zu einem anderen wayIndex führt.
Wie heißt die Station, wo sich Fahrzeug befindet?
Die folgende Funktion liefert die FIS-ID, die in der Konfiguration der Stationen im MapEditor eingetragen wurde. Falls es eine Angabe für das spezifische Gleis gibt, dann wird diese zurück gegeben. Gibt es dagegen nur eine für die gesamte Station, dann wird diese zurück gegeben, andernfalls der interne Name der Station.
Übergeben werden müssen der Funktion wieder die ins Gerät eingegebenen Linien- und Routennummern, wie im vorherigen Beispiel. Sofern keine Station gefunden wurde, wird ein leerer String zurück gegeben:
function AnWelcherHaltestelle_FIS_ID(linie: integer; route: integer): string; var linieStr: string; routeStr: string; wayIndex: integer; section: integer; stnID: string; found: boolean; begin // Der Anfang ist so, wie im vorherigen Beispiel: linieStr := IntToStr(linie); routeStr := IntToStr(route); wayIndex := TimetableGetWayIndex(Self, RBL_Server, linieStr, routeStr); if wayIndex < 0 then begin result := ''; end else begin section := TimetableAtSectionOfWay(Self, wayIndex); // Die Funktion liefert "true", wenn eine Station gefunden wird, in dem Fall // wird die FID-ID in die Variable stnID geschrieben. found := TimetableAtBusstopOfWay(Self, wayIndex, section, stnID); if found then begin result := stnID; end else begin result := ''; end; end; end;
Auch hier wäre es effizienter, wenn der wayindex nur ermittelt wird, wenn sich etwas an der Linien-/Routen-Kombination ändert. Aber auch die section muss zumindest pro Durchlauf nur einmal aktualisiert werden – falls überhaupt so oft, ein "lockeres" Intervall reicht hier auch.
Abstand zwischen zweier Stationen entlang einer Route
Mit dieser Funktion kann – völlig unabhängig davon, wo sich aktuell das Fahrzeug befindet! – ermittelt werden, wie weit zwei beliebige Stationen voneinander entfernt sind (gemessen entlang der Route).
function Stationsabstand(linie: integer; route: integer; FIS_ID_stnA, FIS_ID_stnB: string): single; var linieStr: string; routeStr: string; wayIndex: integer; begin // Der Anfang ist so, wie im vorherigen Beispiel: linieStr := IntToStr(linie); routeStr := IntToStr(route); wayIndex := TimetableGetWayIndex(Self, RBL_Server, linieStr, routeStr); if wayIndex < 0 then begin result := -1; end else begin result := TimetableDistBetweenStns(Self, wayIndex, FIS_ID_stnA, FIS_ID_stnB); end; end;
Wichtig ist hierbei, dass die Stationen anhand der im MapEditor hinterlegten FIS-ID gesucht werden. Verläuft die Route dabei über ein Gleis, welches eine eigene, gegenüber der Station abweichende FIS-ID hat, dann wird jene ID verwendet. Für den Fall, dass die Route über mehrere Station(sgleise) mit derselben FIS-ID verläuft, wird die jeweils erste Station zugrunde gelegt.
Fahrten (mit Zeiten)
Stunden und Minuten in String umwandeln
function TimeToHHMM(time: single): string; var h: integer; min: integer; begin // time liegt zunächst in Tagen vor. Umrechnen in Stunden: time := time * 24.0; // diese Zahl abgerundet entspricht der Stundenangabe einer Digitaluhr: h := trunc(time); result := IntToStrEnh(h, 2, '0') + ':'; // von time werden die Stunden abgezogen, der Rest sind die verbliebenen // Minuten (hierfür muss aber noch mit 60 multipliziert werden:) time := (time - h) * 60.0; // es sollen nur ganze Minuten angezeigt werden, daher wird wiederum // abgerundet und das Ergebnis an den String angehängt: min := trunc(time); result := result + IntToStrEnh(min, 2, '0'); end;
Fahrten eines Umlaufs anzeigen
Für die folgende Funktion stelle man sich vor, dass man zunächst Linie und Kurs eingibt und dann mittels Pfeiltasten die verfügbaren Fahrten durchschaltet, die dann mit erster Abfahrtszeit, Anfangs- und Endstation in einem Textfeld angezeigt werden. index ist dabei die Zahl, die mit den Pfeiltasten um jeweils 1 erhöht oder verringert wird.
Für die folgende Funktion werden die System-Variablen TimeOfDay und Date, die beide im Abschnitt "PUBLIC_VARS" deklariert werden müssen.
function Fahrtstring(linie: integer; kurs: integer; index: integer): string; var linieStr: string; kursStr: string; iTimetable, iTrip, iTourplan, iTour, iTourtrip: integer; anzFahrten, anzStationen: integer; linieDerFahrt, kursDerFahrt, routeDerFahrt: string; globalFahrtIndex: integer; abfahrtszeit: single; stnFISID: string; stnAnkunft, stnAbfahrt: single; begin linieStr := IntToStr(linie); kursStr := IntToStr(kurs); // die folgenden Indizes müssen anhand der eingegebenen Linie/Kurs-Informationen // und Datum/Uhrzeit ermittelt werden. Sie identifizieren den Umlauf und die // Fahrt auf dem Umlauf: TimetableGetTripAndTourIndexByLineCourseDate(Self, Date, TimeOfDay, RBL_Server, linieStr, kursStr, iTimetable, iTrip, iTourplan, iTour, iTourtrip); // Nun soll eine temporäre Liste der Fahrten angelegt werden, die auf dem // soeben ermittelten Umlauf gefahren werden müssen. Die temporäre Liste // besteht im Hintergrund so lange, bis der folgende Befehl erneut ausgeführt // wird. Dann wird die Liste ersetzt. TimetableGenerateTempTripListByTour(Self, iTimetable, iTourplan, iTour); // dann die Länge in eine temporäre Variable schreiben: anzFahrten := TimetableGetTempTripListCount(Self); // Jetzt prüfen, ob der übergebene Index gültig ist. Falls nicht, dann direkt // einen leeren String zurückgeben: if (index < 0) or (index >= anzFahrten) then begin result := ''; end else begin // jetzt können die Daten dieser Fahrt ermittelt werden: TimetableGetTripInfoByTempListIndex(Self, index, linieDerFahrt, kursDerFahrt, routeDerFahrt, globalFahrtIndex, abfahrtszeit); // Mit den hier wiedergegebenen Linie- und Route-Informationen können die weiter // oben beschriebenen Routen-Funktionen (z.B. zwecks Ermittelung, ob man sich // noch auf der Strecke befindet) gefüttert werden. // da die erste und letzte Station dazu geschrieben werden soll, muss auch // die Stationsliste generiert werden. Diese wird ebenso temporär erzeugt // nach dem selben Prinzip wie die Fahrtliste: TimetableGenerateTempStnListByTrip(Self, iTimetable, globalFahrtIndex); // Auch hier die Anzahl der Stationen ermitteln: anzStationen := TimetableGetTempStnListCount(Self); // Falls die Liste leer ist, dann einen leeren String zurückgeben: if anzStationen <= 0 then begin result := ''; end else begin // Nun den String zusammenbauen. Dafür zunächst die Daten der ersten // Station holen: TimetableGetInfoByTempStnListIndex(Self, 0, stnFISID, stnAnkunft, stnAbfahrt); result := TimeToHHMM(abfahrtszeit) + ' : ' + stnFISID + ' => '; // die Daten der letzten Station (anzStationen-1) holen: TimetableGetInfoByTempStnListIndex(Self, anzStationen-1, stnFISID, stnAnkunft, stnAbfahrt); result := result + stnFISID; end; end; end;
Dieses Beispiel strotzt nur so von Ineffizienzen, da hier alle Abfragen in einem "Rutsch" gemacht werden. So muss der Umlauf selbstverständlich nur aufgerufen werden, wenn sich Linie und Kurs ändern. Dementsprechend muss auch die Fahrtenliste nur dann aktualisiert werden und kann die Anzahl der Einträge dieser Liste einmalig in eine globale Variable geschrieben werden anstatt in eine Funktions-Lokale.