Lua-Scripting zur vorbildgerechten Steuerung von Ks-Signalen in EEP 16
Go to file
Nero fa650867fb Demos in einen Ordner weiter oben 2023-07-23 11:16:04 +00:00
Demos Demoanlagen hinzufügen, fixes für Installationsdateien 2023-07-23 11:11:36 +00:00
.gitattributes Windows-Konvention bei Zeilenenden 2022-06-23 11:51:07 +00:00
.gitignore Demoanlagen hinzufügen, fixes für Installationsdateien 2023-07-23 11:11:36 +00:00
Installation.eep Demoanlagen hinzufügen, fixes für Installationsdateien 2023-07-23 11:11:36 +00:00
On.lua Richtiger Umgang mit Signal/Switch unterscheidung 2023-07-23 10:51:11 +00:00
README.md MultiSchalten Modul für komplexere Signalverknüpfungen 2023-07-21 22:54:14 +00:00
Serializer.lua Serializer: Extra Handling, weil Ganzzahlen von tostring() doch manchmal als Kommazahl zurueckgegeben werden 2023-07-21 20:56:42 +00:00
Zuggreifer.lua Zuggreifer: Fixes und Melden-Funktion 2022-07-03 12:59:27 +00:00
demos.ini Demos in einen Ordner weiter oben 2023-07-23 11:16:04 +00:00
mkinstallscript Demos in einen Ordner weiter oben 2023-07-23 11:16:04 +00:00
scripts.ini Demoanlagen hinzufügen, fixes für Installationsdateien 2023-07-23 11:11:36 +00:00

README.md

author title lang
Nero KsKit für Eisenbahn.exe de

KsKit für Eisenbahn.exe

KsKit sind ein paar Lua-Scripte, welche ich auf meinen Anlagen nutze. Von denen habe ich ein paar Dokumentiert, sie sind bisweilen recht nützlich. Es ist auch immer dazugeschrieben, welche Vorraussetzungen man braucht.

Alle Scripte können hier als Zip-Datei heruntergeladen werden, die lässt sich dann wie ein Modell installieren. Die Scripte werden unterhalb vom LUA-Ordner im EEP-Stammverzeichnis installiert.

MultiSchalten: Signalverknüpfung mit mehreren Bedingungen

Bei der Verwendung der Ks-Signale von GK3 hatte ich schnell das Problem, das ich keine Ansteuerung für die Mehrabschnittssignale hatte. MA-Signale haben neben der Hauptsignalfunktion noch eine Vorsignalfunktion.

In einer Fahrstrasse nehme ich das MA-Signal in der Stellung "Halt erwarten" auf, mitsamt eventuellen Geschwindigkeits-Zusatzanzeigern. Wenn das Folgesignal dann von Halt auf Fahrt springt, UND das MA-Signal nicht bereits wieder Halt zeigt, nur dann soll es von Halt erwarten auf Fahrt umgestellt werden können.

Mit den klassischen Signalverknüpfungen ist die Abfrage von diesen zwei Bedingungen nicht möglich.

Daher habe ich eine Lua-basierte Signalverknüpfung geschrieben, die mehrere Bedingungen abfragen kann. Die Verknüpfungen werden dabei in einer Tabelle eingetragen. Jede Zeile in der Tabelle hat dabei zwei gleichartige Listen. Die Listen beinhalten jeweils die ID und die Stellung eines Signals, jeweils abwechselnd. Anstelle von Signalen können auch Weichen eingetragen werden. In der Wenn-Liste werden die Bedigungen eingetragen, sie müssen alle erfüllt sein, damit die Zeile Wirkung zeigt. In der Dann-Liste werden alle zu stellenden Signale und Weichen eingetragen.

require("kskit\\MultiSchalten")

Schalten={
  -- 73 ist unser MA-Signal, 2=Halt erwarten, 3=Fahrt
  -- 16 ist das Folgesignal, 2=Fahrt
  {Wenn={16,2,73,2}, Dann={73,3}},
  -- 1073 und 2073 sind Zusatzanzeiger, wenn wir auf Rot (1) gehen müssen die aus
  {Wenn={73,1}, Dann={1073,1,2073,1}}
}

MultiSchaltenInit(Schalten)

function EEPMain()
  MultiSchaltenMain()
  return 1
end

Die Ausführung erfolgt einmalig nach dem Eintreffen der jeweiligen Callbacks.

Zum das Anlegen der Callbacks wird das On-Modul (folgend) verwendet. Ist ein Signal oder Weiche als Bedingung in der MultiSchalten-Tabelle eingetragen, darf dazu keine EEPOnSignal_-Funktion definiert werden! Es sind die OnSignal/OnSwitch-Funktionen des On-Moduls stattdessen zu benutzen.

Serializer: Tabellen in EEP-Slots speichern

Die Lua-Umgebung wird von EEP in bestimmten Situationen zurückgesetzt und verliert dabei die Inhalte aller Variablen. Daher müssen persistente Werte via EEPSaveData gespeichert und nach dem Reset wieder geladen werden. Bei Zeichenketten und Zahlen ist das kein Problem, das Speichern von Tabellen ist nicht ohne umwege möglich.

KsKit bringt einen Serializer mit, der in der Lage ist, die gängigen Lua-Daten und Tabellen in eine Zeichenkette zu serialisieren.

Funktion serialize

Die serialize-Funktion ist das Herz des Serializers. Sie nimmt ein Argument und gibt eine Zeichenkette zurück.

Der Return-Wert ist gültiges Lua und kann mittels load()-Funktion wieder in die Tabellenform zurückgewandelt werden.

require("kskit\\Serializer")

Tabelle={
  str = "abcdef",
  lst = {1,2,3},
  bol = true
}

print(serialize(Tabelle))
-- Ausgabe: {bol=true,lst={1,2,3},str="abcdef"}

Unterstützt werden Wahrheitswerte, Zeichenketten, Zahlen und einfach verschachtelte Tabellen. Lambda-Funktionen werden ignoriert, ebenso wie rekursiv in sich selbst verschachtelte Tabellen.

Funktion speicherTabelle

Diese Funktion macht genau das, was der Name vermuten lässt. Eine Lua-Tabelle wird in einem EEPSlot abgespeichert.

Das erste Argument zu der Funktion ist dabei die Slotnummer, das zweite Argument eine Lua-Tabelle.

Die Tabelle wird mittels serialize in eine Zeichenkette umgewandelt. Die EEPSlots unterstützen jedoch nicht alle möglichen Zeichen. Daher wird das Zwischenergebnis nochmal in ein urlencode-ähnliches Format umkonvertiert. Dabei werden sämtliche Steuerzeichen und Hochkommas sicher verpackt.

require("kskit\\Serializer")

Tabelle={
  str = "abcdef",
  lst = {1,2,3},
  bol = true
}

speicherTabelle(1, Tabelle)

-- So sieht der Datenslot hinterher in der Lua-Datei aus:
-- [EEPLuaData]
-- DS_1 = "{bol=true,lst={1,2,3},str=%22abcdef%22}"

Funktion ladeTabelle

Das pendant zu speicherTabelle. Als Argument wird die Slotnummer übergeben, als Return-Wert erhält man die vorher gespeicherte Tabelle zurück.

Dabei wird das urlencode wieder entfernt und die Daten mittels load-Funktion wieder eingelesen.

Ist der Slot unleserlich oder wurde in diesen noch keine Tabelle geschrieben, wird eine Warnmeldung in das Ereignisfenster geschrieben und eine leere Tabelle zurückgegeben.

require("kskit\\Serializer")

print(serialize(ladeTabelle(1)))
-- Ausgabe: {bol=true,lst={1,2,3},str="abcdef"}

Praxisbeispiel

Es ist nicht notwendig, eine Tabelle vor jeder Benutzung zu laden und wieder zu speichern.

Viel schneller ist es, die Tabelle als globale Variable zu halten und nur beim Lua-Start einmal einzulesen. Die Tabelle kann dann wie jede andere Tabelle verwendet werden.

Die EEPMain wird innerhalb eines Zyklus zuletzt aufgerufen. Die Kontakte und Callbacks werden davor abgearbeitet. Daher reicht es aus, wenn die Tabelle nur einmalig am Ende der EEPMain zurückgeschrieben wird.

require("kskit\\Serializer")

-- Die Tabelle wird nur beim Starten von Lua einmal geladen
Zugdaten_Slotnummer = 1
Zugdaten = ladeTabelle(Zugdaten_Slotnummer)

-- Diese Funktion wird in Kontakten eingetragen
function Richtung_Merken(Zugname)
  local ok, V = EEPGetTrainSpeed(Zugname)
  Zugdaten[Zugname].V = V
end

function Zug_Wenden(Zugname)
  local Vneu = -Zugdaten[Zugname].V
  EEPSetTrainSpeed(Zugname, Vneu)
  Zugdaten[Zugname].V = Vneu
end

function EEPMain()
  -- andere Dinge tun
  -- ...

  -- Wir sind am Ende des EEP-Zyklus, nur einmal hier speichern
  speicherTabelle(Zugdaten_Slotnummer, Zugdaten)
  return 1
end

On: Multiple Callbacks

Bestimmte Mechanismen erfordern das automatische Registrieren von zahlreichen Signal- oder Weichencallbacks. Bei KsKit ist es erforderlich, das unterschiedliche Module ihre eigenen Callbacks registrieren können, ohne voneinander Wissen zu müssen. Daher bietet das On-Modul die Möglichkeit, beliebig viele Funktionen für einen Callback zu registrieren. Ruft EEP dann den Callback auf, werden alle für diesen Callback registrierten Funktionen aufgerufen.

Das ganze ist sehr bequem, wenn man eine große Anzahl Callbacks mittels for-Schleife aus einer Tabelle erzeugt.

On

So wie das Modul heisst auch die zentrale Funktion. Diese registriert eine Funktion für einen EEP-Callback. Das Anlegen der Callback-Funktion wird bei dem Registrieren der ersten Funktion vorgenommen. Dabei wird, falls notwendig, der Callback von On auch bei EEP registriert. Das händische Aufrufen von EEPRegister* entfällt!

Als erster Parameter wird dabei der Name des Callbacks als String übergeben. Als zweiter Parameter wird eine Funktion übergeben.

Man beachte die Syntax: Es wird eine Funktion ohne Namen verwendet, ebenfalls ist die Funktion innerhalb der Parameterübergabe definiert. Die Funktion wird nicht ausgeführt, sondern selbst als Wert übergeben.

Der übergebenen Funktion werden die Callback-Parameter weitergeleitet, als wäre sie selbst von EEP aufgerufen worden.

require("kskit\\On")

On("EEPOnTrainCoupling", function(Zug_A, Zug_B, Zug_neu)
  print(" Aus "Zug_A.." und "..Zug_B.." wurde "..Zug_neu)
end)

Das ganze in längerer Form mit Umweg über eine Variable:

require("kskit\\On")

callback4=function(Zug_A, Zug_B, Zug_neu)
  print(" Aus "Zug_A.." und "..Zug_B.." wurde "..Zug_neu)
end

On("EEPOnTrainCoupling", callback4)

Diese Funktion wird im Hintergrund von allen anderen Funktionen des On-Moduls benutzt.

OnSignal

Die wohl meistgenutzte Funktion dieses Modules. Als ersten Parameter nimmt sie die ID eines Signales, als zweiten Parameter nimmt sie eine Funktion. Die übergebene Funktion bekommt die Parameter wie üblich übergeben. Der Aufruf von EEPRegisterSignal erfolgt automatisch.

Hier werden zwei anonyme Funktionen an ein Signal gebunden:

require("kskit\\On")

OnSignal(3, function(Stellung)
  print("Signal 3 zeigt "..tonumber(Stellung))
end)

OnSignal(3, function(Stellung)
  print("Signal umgestellt")
end)

Man beachte die sich schließende Klammer nach dem end.

Es können auch vorhandene Funktionen mittels Namen an OnSignal übergeben werden:

require("kskit\\On")

OnSignal(4, print)

In dem Falle wird die neue Signalstellung direkt an print weitergegeben und ausgegeben.

OnSwitch

Das selbe wie OnSignal, nur für Weichen statt für Signale.

Main

Jetzt muss man wissen, die EEPMain ist auch nur ein Callback, mit dem Unterschied, das er ohne Grund immer wieder aufgerufen wird. Dafür gibt es in diesem Modul eine Main-Funktion. Diese Funktion nimmt nur einen Parameter, jener beinhaltet die Funktion, welche an die EEPMain gebunden werden soll.

require("kskit\\On")
require("Zugtuersteuerung_FS2.lua")

-- Definitionen für RUS-Packet hier

-- RUS-Packet von Parry36 aktivieren
Main(inEEPMain)
-- Zugtuersteuerung vom Fried aktivieren
Main(BewegeZugtueren)

Main(...) ist auch nichts weiteres als ein Bequemlichkeits-Aufruf von On("EEPMain", ...)