Kochbuch: Fahrpläne im Script

Aus LOTUS Wiki DE
Version vom 21. Januar 2025, 22:55 Uhr von DrBlackError (Diskussion | Beiträge) (Updated from LOTUS Lexicon on 2025-01-21)
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)
Wechseln zu: Navigation, Suche

Vorlage:ArticleMetadata

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.

1 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.

2 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-[[1]]-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`:


Code

 1. [[2]]var
 2. [[3]] NotFirstRun: boolean;
 3. [[4]] RBL_Server: string;
 4. [[5]]...
 5. [[6]]
 6. [[7]]procedure Initialize;
 7. [[8]]begin
 8. [[9]] NotFirstRun := false;
 9. [[10]] ...
 10. [[11]]end;
 11. [[12]]
 12. [[13]]procedure SimStep;
 13. [[14]]begin
 14. [[15]] if not NotFirstRun then
 15. [[16]] begin
 16. [[17]] RBL_Server := PIS_GetITCSServer(Self);
 17. [[18]] ... // weitere Dinge, die nur einmalig aus SimStep heraus aufgerufen werden sollen
 18. [[19]] NotFirstRun := true;
 19. [[20]] end;
 20. [[21]] ...
 21. [[22]]end;

Alles anzeigen

3 Routen (keine Zeiten)

3.1 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 ...

Code

 1. [[23]]function WoAufDerRoute(linie: integer; route: integer): integer;
 2. [[24]]var
 3. [[25]] linieStr: string;
 4. [[26]] routeStr: string;
 5. [[27]] wayIndex: integer; // interner Index von Route/Wege
 6. [[28]]begin
 7. [[29]] // Die Prozeduren arbeiten zwecks der Flexibilität mit Strings.
 8. [[30]] // Daher müssen die eingegebenen Daten zunächst in Strings umgewandelt werden:
 9. [[31]] linieStr := IntToStr(linie);
 10. [[32]] routeStr := IntToStr(route);
 11. [[33]] wayIndex := TimetableGetWayIndex(Self, RBL_Server, linieStr, routeStr);
 12. [[34]] // Falls der Index negativ ist, dann ist die eingegebene Route nicht vorhanden
 13. [[35]] // Dann soll einfach -1 zurück gegeben werden:
 14. [[36]] if wayIndex < 0 then
 15. [[37]] begin
 16. [[38]] result := -1;
 17. [[39]] end
 18. [[40]] else
 19. [[41]] begin
 20. [[42]] result := TimetableAtSectionOfWay(Self, wayIndex); 
 21. [[43]] end; 
 22. [[44]]end;

Alles anzeigen

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.

3.2 Wie heißt die Station, wo sich Fahrzeug befindet?

Die folgende Funktion liefert die [[45]]-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:

Code

 1. [[46]]function AnWelcherHaltestelle_FIS_ID(linie: integer; route: integer): string;
 2. [[47]]var
 3. [[48]] linieStr: string;
 4. [[49]] routeStr: string;
 5. [[50]] wayIndex: integer;
 6. [[51]] section: integer;
 7. [[52]] stnID: string;
 8. [[53]] found: boolean;
 9. [[54]]begin
 10. [[55]] // Der Anfang ist so, wie im vorherigen Beispiel:
 11. [[56]] linieStr := IntToStr(linie);
 12. [[57]] routeStr := IntToStr(route);
 13. [[58]] wayIndex := TimetableGetWayIndex(Self, RBL_Server, linieStr, routeStr);
 14. [[59]] if wayIndex < 0 then
 15. [[60]] begin
 16. [[61]] result := ;
 17. [[62]] end
 18. [[63]] else
 19. [[64]] begin
 20. [[65]] section := TimetableAtSectionOfWay(Self, wayIndex);
 21. [[66]] // Die Funktion liefert "true", wenn eine Station gefunden wird, in dem Fall
 22. [[67]] // wird die FID-ID in die Variable stnID geschrieben. 
 23. [[68]] found := TimetableAtBusstopOfWay(Self, wayIndex, section, stnID);
 24. [[69]] if found then
 25. [[70]] begin
 26. [[71]] result := stnID;
 27. [[72]] end
 28. [[73]] else
 29. [[74]] begin
 30. [[75]] result := ;
 31. [[76]] end; 
 32. [[77]] end; 
 33. [[78]]end;

Alles anzeigen

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.

3.3 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).

Code

 1. [[79]]function Stationsabstand(linie: integer; route: integer; FIS_ID_stnA, FIS_ID_stnB: string): single;
 2. [[80]]var
 3. [[81]] linieStr: string;
 4. [[82]] routeStr: string;
 5. [[83]] wayIndex: integer;
 6. [[84]]
 7. [[85]]begin
 8. [[86]] // Der Anfang ist so, wie im vorherigen Beispiel:
 9. [[87]] linieStr := IntToStr(linie);
 10. [[88]] routeStr := IntToStr(route);
 11. [[89]] wayIndex := TimetableGetWayIndex(Self, RBL_Server, linieStr, routeStr);
 12. [[90]] if wayIndex < 0 then
 13. [[91]] begin
 14. [[92]] result := -1;
 15. [[93]] end
 16. [[94]] else
 17. [[95]] begin
 18. [[96]] result := TimetableDistBetweenStns(Self, wayIndex, FIS_ID_stnA, FIS_ID_stnB); 
 19. [[97]] end; 
 20. [[98]]end;

Alles anzeigen

Wichtig ist hierbei, dass die Stationen anhand der im MapEditor hinterlegten [[99]]-ID gesucht werden. Verläuft die Route dabei über ein Gleis, welches eine eigene, gegenüber der Station abweichende [[100]]-ID hat, dann wird jene ID verwendet. Für den Fall, dass die Route über mehrere Station(sgleise) mit derselben [[101]]-ID verläuft, wird die jeweils erste Station zugrunde gelegt.

4 Fahrten (mit Zeiten)

4.1 Stunden und Minuten in String umwandeln

Code

 1. [[102]]function TimeToHHMM(time: single): string;
 2. [[103]]var
 3. [[104]] h: integer;
 4. [[105]] min: integer;
 5. [[106]]begin
 6. [[107]] // time liegt zunächst in Tagen vor. Umrechnen in Stunden:
 7. [[108]] time := time * 24.0;
 8. [[109]] // diese Zahl abgerundet entspricht der Stundenangabe einer Digitaluhr:
 9. [[110]] h := trunc(time);
 10. [[111]] result := IntToStrEnh(h, 2, '0') + ':';
 11. [[112]] // von time werden die Stunden abgezogen, der Rest sind die verbliebenen
 12. [[113]] // Minuten (hierfür muss aber noch mit 60 multipliziert werden:)
 13. [[114]] time := (time - h) * 60.0;
 14. [[115]] // es sollen nur ganze Minuten angezeigt werden, daher wird wiederum
 15. [[116]] // abgerundet und das Ergebnis an den String angehängt:
 16. [[117]] min := trunc(time);
 17. [[118]] result := result + IntToStrEnh(min, 2, '0');
 18. [[119]]end;

Alles anzeigen

4.2 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.

Code

 1. [[120]]function Fahrtstring(linie: integer; kurs: integer; index: integer): string;
 2. [[121]]var
 3. [[122]] linieStr: string;
 4. [[123]] kursStr: string;
 5. [[124]] iTimetable, iTrip, iTourplan, iTour, iTourtrip: integer;
 6. [[125]] anzFahrten, anzStationen: integer; 
 7. [[126]] linieDerFahrt, kursDerFahrt, routeDerFahrt: string;
 8. [[127]] globalFahrtIndex: integer;
 9. [[128]] abfahrtszeit: single; 
 10. [[129]] stnFISID: string;
 11. [[130]] stnAnkunft, stnAbfahrt: single; 
 12. [[131]]begin
 13. [[132]] linieStr := IntToStr(linie);
 14. [[133]] kursStr := IntToStr(kurs);
 15. [[134]] // die folgenden Indizes müssen anhand der eingegebenen Linie/Kurs-Informationen
 16. [[135]] // und Datum/Uhrzeit ermittelt werden. Sie identifizieren den Umlauf und die
 17. [[136]] // Fahrt auf dem Umlauf:
 18. [[137]] TimetableGetTripAndTourIndexByLineCourseDate(Self, Date, TimeOfDay, RBL_Server, linieStr, kursStr, iTimetable, iTrip, iTourplan, iTour, iTourtrip);
 19. [[138]]
 20. [[139]] // Nun soll eine temporäre Liste der Fahrten angelegt werden, die auf dem
 21. [[140]] // soeben ermittelten Umlauf gefahren werden müssen. Die temporäre Liste
 22. [[141]] // besteht im Hintergrund so lange, bis der folgende Befehl erneut ausgeführt
 23. [[142]] // wird. Dann wird die Liste ersetzt.
 24. [[143]] TimetableGenerateTempTripListByTour(Self, iTimetable, iTourplan, iTour);
 25. [[144]]
 26. [[145]] // dann die Länge in eine temporäre Variable schreiben: 
 27. [[146]] anzFahrten := TimetableGetTempTripListCount(Self);
 28. [[147]]
 29. [[148]] // Jetzt prüfen, ob der übergebene Index gültig ist. Falls nicht, dann direkt
 30. [[149]] // einen leeren String zurückgeben:
 31. [[150]] if (index < 0) or (index >= anzFahrten) then
 32. [[151]] begin
 33. [[152]] result := ;
 34. [[153]] end
 35. [[154]] else
 36. [[155]] begin
 37. [[156]] // jetzt können die Daten dieser Fahrt ermittelt werden:
 38. [[157]] TimetableGetTripInfoByTempListIndex(Self, index, linieDerFahrt, kursDerFahrt, routeDerFahrt, globalFahrtIndex, abfahrtszeit);
 39. [[158]]
 40. [[159]] // Mit den hier wiedergegebenen Linie- und Route-Informationen können die weiter
 41. [[160]] // oben beschriebenen Routen-Funktionen (z.B. zwecks Ermittelung, ob man sich
 42. [[161]] // noch auf der Strecke befindet) gefüttert werden.
 43. [[162]]
 44. [[163]] // da die erste und letzte Station dazu geschrieben werden soll, muss auch
 45. [[164]] // die Stationsliste generiert werden. Diese wird ebenso temporär erzeugt
 46. [[165]] // nach dem selben Prinzip wie die Fahrtliste:
 47. [[166]] TimetableGenerateTempStnListByTrip(Self, iTimetable, globalFahrtIndex);
 48. [[167]]
 49. [[168]] // Auch hier die Anzahl der Stationen ermitteln:
 50. [[169]] anzStationen := TimetableGetTempStnListCount(Self);
 51. [[170]]
 52. [[171]] // Falls die Liste leer ist, dann einen leeren String zurückgeben:
 53. [[172]] if anzStationen <= 0 then
 54. [[173]] begin
 55. [[174]] result := ;
 56. [[175]] end
 57. [[176]] else
 58. [[177]] begin
 59. [[178]] // Nun den String zusammenbauen. Dafür zunächst die Daten der ersten
 60. [[179]] // Station holen:
 61. [[180]] TimetableGetInfoByTempStnListIndex(Self, 0, stnFISID, stnAnkunft, stnAbfahrt);
 62. [[181]] result := TimeToHHMM(abfahrtszeit) + ' : ' + stnFISID + ' => ';
 63. [[182]] // die Daten der letzten Station (anzStationen-1) holen:
 64. [[183]] TimetableGetInfoByTempStnListIndex(Self, anzStationen-1, stnFISID, stnAnkunft, stnAbfahrt);
 65. [[184]] result := result + stnFISID;
 66. [[185]] end; 
 67. [[186]] end; 
 68. [[187]]end;

Alles anzeigen

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.


5 to be continued...