kskit/README.md

277 lines
8.7 KiB
Markdown
Raw Normal View History

2022-04-07 19:46:11 +02:00
---
author: Nero
title: KsKit für Eisenbahn.exe
2022-07-02 22:54:56 +02:00
lang: de
2022-04-07 19:46:11 +02:00
---
2022-04-07 18:39:46 +02:00
# KsKit für Eisenbahn.exe
2023-07-22 00:26:42 +02:00
KsKit sind ein paar Lua-Scripte, welche ich auf meinen Anlagen nutze.
2022-07-03 22:47:27 +02:00
Von denen habe ich ein paar Dokumentiert, sie sind bisweilen recht nützlich.
Es ist auch immer dazugeschrieben, welche Vorraussetzungen man braucht.
2022-06-26 16:15:50 +02:00
Alle Scripte können [hier](https://github.com/nero/kskit/archive/refs/heads/master.zip) als Zip-Datei heruntergeladen werden, die lässt sich dann wie ein Modell installieren.
2023-07-22 00:26:42 +02:00
Die Scripte werden unterhalb vom LUA-Ordner im EEP-Stammverzeichnis installiert.
2023-07-23 14:56:28 +02:00
## On-Modul
2023-07-23 14:56:28 +02:00
Bisher konnte an jedes Signal oder Weiche nur ein Callback gebunden werden.
Das ist kein Problem, wenn man das Anlagenscript komplett selber schreibt.
Sobald man aber mehrere Module hat, welche unabhängig voneinander Callbacks definieren, braucht man einen Mechanismus, um die alle unter einen Hut zu kriegen.
2023-07-23 14:56:28 +02:00
Als Lösung dafür erfolgt mit dem `On`-Modul ein Paradigmenwechsel.
Anstelle den Callback direkt als Funktion zu definieren, wird der Callbackname und eine anonyme Funktion an das Modul übergeben.
2023-07-23 14:56:28 +02:00
Die bisherige Syntax für Callbacks und die EEPMain sah wie folgt aus:
2023-07-23 14:56:28 +02:00
```
EEPRegisterSignal(5)
function EEPOnSignal_5(Stellung)
...
end
2023-07-23 14:56:28 +02:00
function EEPMain()
...
end
```
2023-07-23 14:56:28 +02:00
Die Verwendung des On-Modules erfordert eine andere Syntax.
2023-07-23 14:56:28 +02:00
```
require("kskit\\On")
OnSignal(5, function(Stellung)
...
end)
Main(function()
...
end)
```
Die Callback-Funktion wird nicht mehr auf der globalen Ebene definiert, sondern als Argument an eine andere Funktion übergeben.
Daher auch die sich schließende Klammer nach dem `end` - sie schließt den Funktionsaufruf von OnSignal/Main ab.
Das ist umständlich, das ist ungewohnt, ermöglicht aber einige Vorteile:
- Die On-Funktionen können mehrmals aufgerufen werden, mehrere Funktionen an einem EEP Callback sind kein Problem
- Module können unabhängig voneinander Code durch einen Callback ausführen lassen
- Module können unabhängig voneinander ihre eigene EEPMain definieren
Ebenfalls werden zwei größere Fehlerquellen ausgeschlossen:
- Der Aufruf von EEPRegisterSignal/EEPRegisterSwitch wird vom `On`-Module automatisch gemacht
- Bei dem Anlegen von Signal/Weichencallbacks wird die Existenz dieser Objekte überprüft
Zum Beispiel können eine Vorsignalsteuerung und eine Türsteuerung beide einen Callback auf das Abfahrtssignal registrieren, ohne voneinander zu wissen zu müssen.
### 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
2023-07-23 14:56:28 +02:00
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:
```
2023-07-23 14:56:28 +02:00
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.
2023-07-23 14:56:28 +02:00
### Main
2023-07-23 14:56:28 +02:00
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", ...)`
2023-07-22 00:26:42 +02:00
## Serializer: Tabellen in EEP-Slots speichern
2022-07-03 22:47:27 +02:00
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
2022-07-03 22:47:27 +02:00
Die `serialize`-Funktion ist das Herz des Serializers.
2022-07-03 22:47:27 +02:00
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.
```
2023-07-22 00:26:42 +02:00
require("kskit\\Serializer")
2022-07-03 22:47:27 +02:00
Tabelle={
str = "abcdef",
lst = {1,2,3},
bol = true
}
print(serialize(Tabelle))
2022-07-03 22:47:27 +02:00
-- 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.
2022-07-03 22:47:27 +02:00
### 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.
2022-07-03 22:47:27 +02:00
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.
```
2023-07-22 00:26:42 +02:00
require("kskit\\Serializer")
2022-07-03 22:47:27 +02:00
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.
```
2023-07-22 00:26:42 +02:00
require("kskit\\Serializer")
2022-07-03 22:47:27 +02:00
2023-07-22 00:26:42 +02:00
print(serialize(ladeTabelle(1)))
2022-07-03 22:47:27 +02:00
-- Ausgabe: {bol=true,lst={1,2,3},str="abcdef"}
```
2022-07-03 22:59:22 +02:00
### Praxisbeispiel
Es ist nicht notwendig, eine Tabelle vor jeder Benutzung zu laden und wieder zu speichern.
2022-07-03 22:59:22 +02:00
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.
```
2023-07-22 00:26:42 +02:00
require("kskit\\Serializer")
2022-07-03 22:59:22 +02:00
-- 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
```