• Dateigröße:4 KB
  • Bereitgestellt:07.03.2017
  • Update:27.12.2018
  • Downloads:2029

SlotNames vereinfacht den Umgang mit den EEP-Speicherslots in Lua. Statt komplizierten Funktionsaufrufen (EEPLoadData bzw. EEPSaveData) kannst du auf die Slots ganz einfach über eine Tabelle zugreifen – sowohl lesend als auch schreibend. Und statt der unübersichtlichen Slot-Nummer kannst du die Slots nun auch über Namen ansprechen (wenn du den Namen zuvor eine Slot-Nummer zugeordnet hast; das geht leider noch nicht automatisch).

Schnellstartanleitung

1. Installation

Nach dem Download die zip-Datei in EEP über den Menüpunkt „Modelle installieren“ installieren (gibt es erst ab EEP13), ansonsten die zip-Datei entpacken und die Installation.eep aufrufen, oder die SlotNames_BH2.lua von Hand ins EEP-Verzeichnis in den Unterordner LUA kopieren.

2. Einbinden

Füge diese Zeile an den Anfang des Anlagen-Skripts ein (die zusätzlichen runden Klammern am Ende sind wichtig!):

Slot, SlotMapping, SlotFuncs = require("SlotNames_BH2")()

3. Konfigurieren

Um über sprechende Namen auf die Slots zugreifen zu können, muss diesen Namen erstmal eine Slotnummer zugeordnet werden. Dies geht, indem die Slotnummern in der Tabelle SlotMapping unter dem gewünschten Namen gespeichert werden, zum Beispiel so:

SlotMapping.Zaehler = 1
SlotMapping.Bahnhof = {
  West = 100,
  Ost = 101,
}
SlotMapping.Gleis = { 201, 202, 203, 204 }

SlotFuncs.checkMapping() -- funktioniert auch ohne, hilft aber beim Fehler-Finden

Ich empfehle, die gesamte Konfiguration an einer zentralen Stelle vorzunehmen (z.B. direkt nach dem require()-Aufruf), damit du nicht den Überblick verlierst, welche Slotnummern schon vergeben wurden. Weiter unten gibt es noch nähere Infos zur Konfiguration.

4. Verwenden

Nun kannst du über die Tabelle Slot und die von dir vergebenen Namen einfach auf die Slots zugreifen. Das geht sowohl lesend (Slot.Zaehler irgendwo als Wert verwenden) als auch schreibend (mit Slot.Zaehler = 42 einen neuen Wert zuweisen).

Nochmal im direkten Vergleich: Um den Wert 42 in Slot 1 zu speichern, musste man bisher schreiben:

EEPSaveData(1, 42)

Jetzt geht es (nach der entsprechenden Konfiguration, siehe oben) mit:

Slot.Zaehler = 42

Das Laden eines Wertes (z.B. um ihn mit print auszugeben) war noch umständlicher:

meinErfolg, meinWert = EEPLoadData(1)
print(meinWert)

Jetzt kann auf die zusätzlichen Variablen verzichtet werden:

print(Slot.Zaehler)

Und im Gegensatz zu früher weiß man jetzt auch gleich, worum es überhaupt geht.

EEP stellt (ab Version 11, vorher gab es das gar nicht) 1000 Slots zur Verfügung. Durch Schreiben von nil wird ein Slot gelöscht. Das war auch mit EEPSaveData so, und funktioniert auch genau wie bei normalen Lua-Tabellen. Das Auslesen eines nicht vorhandenen bzw. eines leeren Slots ergibt wiederum nil. Das ist wieder wie bei normalen Lua-Tabellen, aber anders als bei EEPLoadData: Dort musste der erste Rückgabewert geprüft werden.

Wenn auf einen Slot-Namen zugegriffen wird, dem keine Slot-Nummer zugeordnet wurde, wird das Skript mit einer Fehlermeldung angehalten, die die Stelle im Code enthält, an der der Fehler aufgetreten ist.

Code-Beispiel

Ich habe das Standard-Skript, das EEP bei einer neuen Anlage erzeugt, mal so umgeschrieben, dass der Zähler I, der laufend im Ereignisfenster ausgegeben wird, durch einen Slot ersetzt wird. Dieser fängt dann nicht bei jedem Neuladen des Skripts oder der Anlage bei 0 an zu zählen, sondern macht (sofern Zeile 6 auskommentiert wurde) beim letzten Wert weiter.

Slot, SlotMapping, SlotFuncs = require("SlotNames_BH2")()

SlotMapping.Zaehler = 1
SlotFuncs.checkMapping()

Slot.Zaehler=0  -- nach dem ersten Durchlauf auskommentieren/löschen
clearlog()

print("Hey let's start, EEP Version is: ", EEPVer)

function EEPMain()
    print(Slot.Zaehler)
    Slot.Zaehler=Slot.Zaehler+1
    return 1
end

Der durch SlotNames vereinfachte Zugriff wird in diesem Skript gleich viermal verwendet: In Zeile 6 wird ein Wert (die 0) in den am Anfang angegebenen Slot gespeichert. In Zeile 12 wird dieser Wert wieder ausgelesen. In Zeile 13 wird der Wert nochmal ausgelesen, um eins erhöht und gleich wieder gespeichert.

Ein ausführlicheres Beispiel in Form eine Demo-Anlage folgt in Kürze.

Konfiguration im Detail

Bei der Einbindung werden drei Tabellen zurückgegeben und in Variablen gespeichert. Über die erste Variable können Daten aus den Slots gelesen und in diese geschrieben werden. Die dritte Tabelle enthält Hilfsfunktionen. Als zweiter Wert wird eine Tabelle zurückgegeben, die intern für die Zuordnung der Namen zu den Slotnummern verwendet wird. Anfangs ist diese Tabelle leer. Um nun auf Slots zugreifen zu können, muss die Tabelle gefüllt werden. Dabei gibt der Schlüssel immer den Namen an, über den zugegriffen werden soll, und der Wert die Nummer des zu verwendenden Slots. Alternativ kann der Wert auch wiederum eine Tabelle sein, die weitere Name-Slotnummer-Zuordnungen enthält. So können die Slots einfach gruppiert werden.

Da es sich beim SlotMapping um eine ganz normale Lua-Tabelle handelt, können alle in Lua bekannten Arten verwendet werden, um sie zu befüllen. Einzelne Einträge können mit SlotMapping.Name = SlotNr gesetzt werden. Für Untereinträge sind auch sogenannte „Tabellenkonstruktoren“ möglich. Auch das Setzen von Einträgen in einer Schleife kann sinnvoll sein.

Im Folgenden findst du einige Beispiele, wie die Zuordnungstabelle befüllt werden kann. Da Lua einiges an syntaktischem Zucker bietet, gibt es oft mehrere mögliche Schreibweisen. Hier tun jeweils zwei untereinander stehende Zeilen immer genau das gleiche:

SlotMapping["Zaehler"] = 1
SlotMapping.Zaehler = 1
--> Zugriff über Slot.Zaehler (oder Slot["Zaehler"])

-- Tabellenkonstruktor mit Strings ("Namen") als Schlüssel
SlotMapping.Bahnhof = { ["West"] = 100, ["Ost"] = 101 }
SlotMapping.Bahnhof = { West = 100, Ost = 101 }
--> Zugriff über Slot.Bahnhof.Ost (oder Slot["Bahnhof"]["Ost"] oder Slot.Bahnhof["Ost"] oder Slot["Bahnhof"].Ost)

-- Tabellenkonstruktor mit Zahlen als Schlüssel, werden automatisch durchnummeriert
SlotMapping.Gleis = { [1] = 201, [2] = 202, [3] = 203, [4] = 204 }
SlotMapping.Gleis = { 201, 202, 203, 204 }
--> Zugriff über Slot.Gleis[4] (oder Slot["Gleis"][4])

-- Das gleiche wie oben, jetzt in einer Schleife erzeugt
SlotMapping.Gleis = {} -- die Tabelle muss erst angelegt werden, bevor sie befüllt werden kann
for i = 1,4 do
  SlotMapping.Gleis[i] = 200+i
end
--> Zugriff über Slot.Gleis[4] (oder Slot["Gleis"][4])

Aber aufgepasst: Mit einem Tabellenkonstruktor wird eine neue Tabelle erzeugt, und keine bestehende verändert. Mit ...

SlotMapping = {
  Zaehler = 1,
  Bahnhof = { West = 100, Ost = 101 },
  Gleis = { 201, 202, 203, 204 },
}

... bleibt die eigentliche SlotMapping-Tabelle unverändert. Stattdessen wird eine neue Tabelle erzeugt, die nun über SlotMapping erreichbar ist. Der interne Code weiß von dieser neuen Tabelle aber nichts. Um sie doch zu verwenden, kann diese neue Tabelle mit SlotFuncs.setMapping(neueTabelle) (siehe unten) explizit „bekannt gemacht“ werden.

Um Fehler wie z.B. doppelt vergebene Slotnummern frühzeitig zu erkennen, rate ich dringend dazu, nach der Konfiguration die folgende Zeile einzufügen:

SlotFuncs.checkMapping()

Solange die Zuordnung korrekt ist, hat dieser Funktionsaufruf keine Auswirkungen. Falls jedoch aus Versehen beispielsweise zweimal die gleiche Slotnummer vergeben wurde, wird das Skript an dieser Stelle angehalten. So ist der Fehler deutlich einfacher zu finden, als wenn sich irgendwo ein Teil des Skripts „komisch“ verhält, weil die Slot-Daten an einer ganz anderen Stelle unbeabsichtigt überschrieben wurden.

Funktionen

Die folgenden Funktionen stehen als Einträge der Tabelle zur Verfügung, die bei der Einbindung als dritter Wert zurückgegeben wurde (normalerweise SlotFuncs genannt).

addDefaultMapping()

Ergänzt das Slot-Mapping um die Einträge 1 bis 1000, die auf den entsprechenden Slot zeigen. Dann kann (wie beim SlotSugar) über Slot[i] auf Slot i zugegriffen werden kann. Bestehende Einträge werden nicht überschrieben.

Keine Parameter, kein Rückgabewert.

removeDefaultMapping()

Löscht die mit addDefaultMapping() erzeugten Mapping-Einträge wieder. Es werden nur die Einträge zwischen 1 und 1000 gelöscht, die auf den entsprechenden Slot verweisen.

Keine Parameter, kein Rückgabewert.

setMapping(newMapping)

Überschreibt die interne Zuordnungs-Tabelle mit der übergebenen neuen Tabelle. Kann sinnvoll sein, wenn nicht nur einzelne Einträge geändert, sondern die gesamte Tabelle überschrieben wurde.

Kein Rückgabewert.

checkMapping(options)

Prüft das aktuelle Namen-Slotnummer-Zuordnung auf Fehler. Momentan werden drei Dinge geprüft: Jeder Eintrag ist eine Zahl, diese liegt zwischen 1 und 1000 und ist nicht bereits unter einem anderen Namen vergeben.

Es kann eine Tabelle als Parameter übergeben werden, die das Verhalten ändert. Folgende Tabelleneinträge sind möglich:

  • checkNumber: true (Standardwert) oder false: Ob geprüft werden soll, dass es sich bei den Eintragen um Zahlen handelt.
  • checkRange: true (Standardwert) oder false: Ob geprüft werden soll, dass sich alle Zahlen im Bereich von 1 bis 1000 befinden.
  • checkDuplicates: true (Standardwert) oder false: Ob geprüft werden soll, dass keine Slotnummer mehrfach vergeben wurde.
  • ignoreDefaultMapping: true oder false (Standardwert): Ob Default-Mapping-Einträge (siehe addDefaultMapping) ignoriert werden sollen (durch diese Einträge kommt es fast zwangsläufig zu doppelt vergebenen Slot-Nummern).
  • printSuccess: true oder false (Standardwert): Ob eine Erfolgsmeldung ins Ereignisfenster geschrieben werden soll, falls keine Fehler gefunden wurden. Falls Fehler gefunden wurden, hat diese Option keinen Effekt.
  • results: "print", "error" (Standardwert) oder "return": Was mit gefundenen Fehlern passieren soll: Bei "return" wird nichts im Ereignisfenster ausgegeben, stattdessen wird eine Tabelle mit allen Funden zurückgegeben (siehe unten). Bei "print" und "error" werden alle gefundenen Fehler im Ereignisfenster ausgegeben. Bei "error" wird (falls es Fehler gab) ein Lua-Fehler erzeugt und somit das ganze Skript angehalten. Damit ist sichergestellt, dass das restliche Skript nur läuft, wenn es keine Fehler in der Slot-Zuordnung gibt.

Beispielaufruf:

SlotFuncs.checkMapping({ ignoreDefaultMapping = true, printSuccess = true, results = "print" })

Rückgabewert: Wenn die results-Option "return" ist, eine Tabelle mit allen gefundenen Fehlern (Format siehe unten). Ansonsten: true, falls keine Fehler gefunden wurden, andernfalls false.

{
  { path={"Gleis", 1}, message='Slot 201 wird schon von "andererEintrag" verwendet' },
  -- ...
}

getVersion()

Gibt die SlotNames-Version als Tabelle {0,1} zurück.


Die folgenden Funktionen werden intern verwendet. Sie sind hier nur der Vollständigkeit halber erwähnt.

lookupSlotNr(...)

Gibt die im Slot-Mapping definierte Slotnummer für den gegebenen Pfad zurück. Jeder Teil des Pfades muss als einzelnes Argument angegeben werden.

SlotFuncs.lookupSlotNr("Gleis", 1) -- gibt 201 zurück

getPathString(path)

Gibt den gegebenen Pfad (durch Punkte getrennt) als String zurück. Der Pfad muss als Tabelle übergeben werden:

SlotFuncs.getPathString({"Gleis", 1}) -- gibt "Gleis.1" zurück

Hinweise zum Umstieg auf SlotNames

Umstieg vom SlotSugar

Der SlotSugar (siehe unten) als Vorgänger von SlotNames funktioniert bereits ähnlich, allerdings wurde statt über Namen direkt per Slotnummer auf die Tabellen-Einträge zugegriffen. Dementsprechend gab es auch keine Kontrolle über mögliche Doppelbelegungen.

Für die Übergangsphase gibt es zwei Möglichkeiten:

Parallelbetrieb

SlotNames und SlotSugar lassen sich theoretisch auch gleichzeitig verwenden. Dabei muss man nur aufpassen, dass die Funktionalität nicht beidesmal in der gleichen Variable (in den Beispielen immer Slot) gespeichert wird. Da Slot vermutlich schon vom SlotSugar verwendet wird, könnte für SlotNames ein anderer Name gewählt werden:

Slot = require("SlotSugar_BH2")
NewSlot, SlotMapping, SlotFuncs = require("SlotNames_BH2")()

Dann müssen alle „benannten Slot-Zugriffe“ natürlich über NewSlot erfolgen.

Integration (empfohlen)

SlotNames kann so konfiguriert werden, dass auch der bisherige SlotSugar-Code damit funktioniert. Dazu ist es nötig, dass jeder Zahl, über die zugegriffen wird, dieselbe Zahl als Slotnummer zugeordnet wird. Die Funktion SlotFuncs.addDefaultMapping() tut genau dies automatisch. Um in der Übergangszeit Fehlermeldungen zu vermeiden, sollten die checkMapping-Optionen um ignoreDefaultMapping = true ergänzt werden:

Slot, SlotMapping, SlotFuncs = require("SlotNames_BH2")()
SlotFuncs.addDefaultMapping()
-- hier kommt die SlotMapping-Konfiguration hin
SlotFuncs.checkMapping({ ignoreDefaultMapping = true })

Wenn alle Slots auf das neue System umgestellt sind, kann der Aufruf von SlotFuncs.addDefaultMapping() wieder gelöscht werden.

Umstieg von EEPLoadData und EEPSaveData

Da EEPLoadData und EEPSaveData auch weiterhin verwendet werden können, gibt es für die Übergangsphase nichts besonderes zu beachten. Für jedes Vorkommen von EEPLoadData(nr) bzw. EEPSaveData(nr, wert) muss die Slotnummer in die Zuordnungstabelle eingetragen werden (bei der Suche nach einem geeigneten Namen merkst du auch, ob dein Code gut genug dokumentiert war), anschließend kann der Funktionsaufruf durch die einfachere Variante mit Zuweisung bzw. Wert-Verwendung ersetzt werden (wie in der Schnellstartanleitung).

Changelog

v0.1.1 vom 27.12.2018:
  • Zeichenkodierung korrigiert. Jetzt sollten auch die Umlaute in Fehlermeldungen richtig angezeigt werden.
  • Funktional hat sich nichts geändert.
v0.1 vom 07.03.2017:
  • Einfacheres Speichern in und Laden aus den von EEP bereitgestellten Slots
  • Slot-Zugriff über Namen statt Nummern, die Zuordnung der Namen zu den Slotnummern ist frei konfigurierbar
  • Funktion zur Überprüfung dieser Zuordnungen

SlotSugar

  • Dateigröße:1 KB
  • Bereitgestellt:07.01.2017
  • Downloads:1440

Der SlotSugar vereinfacht den Umgang mit den EEP-Speicherslots in Lua. Statt komplizierten Funktionsaufrufen (EEPLoadData bzw. EEPSaveData) kannst du auf die Slots ganz einfach über eine Tabelle zugreifen – sowohl lesend als auch schreibend.

Und falls du dich jetzt fragst, was Zucker mit Slots zu tun hat...

Was ist „syntactic sugar“?

Als „Syntaktischen Zucker„ (oder auf englisch „syntactic sugar“) bezeichnet man Sprachkonstrukte bzw. Eigenschaften einer Programmiersprache, die zwar keine neuen Möglichkeiten bieten, vorhandene aber leichter zugänglich machen. Lua enthält bereits einiges an syntaktischem Zucker: Beispielsweise kann man statt Tabelle["Eintrag"] auch Tabelle.Eintrag schreiben.

Mein SlotSugar ist ähnlich: Er bietet keine neuen Funktionen, sondern macht nur bereits vorhandene Funktionen leichter zugänglich.

Schnellstartanleitung

1. Installation

Nach dem Download die zip-Datei in EEP über den Menüpunkt „Modelle installieren“ installieren (gibt es erst ab EEP13), ansonsten die zip-Datei entpacken und die Installation.eep aufrufen, oder die SlotSugar_BH2.lua von Hand ins EEP-Verzeichnis in den Unterordner LUA kopieren.

2. Einbinden

Füge diese Zeile an den Anfang des Anlagen-Skripts ein:

Slot = require("SlotSugar_BH2")

3. Verwenden

Nun stehen alle Slots in der Tabelle Slot zur Verfügung. Der erste Slot wird mit Slot[1] angesprochen, und so weiter. Das geht sowohl lesend (Slot[1] irgendwo als Wert verwenden) als auch schreibend (mit Slot[1]=42 einen neuen Wert zuweisen).

Nochmal im direkten Vergleich: Um den Wert 42 in Slot 1 zu speichern, musste man bisher schreiben:

EEPSaveData(1, 42)

Jetzt geht es mit:

Slot[1] = 42

Das Laden eines Wertes (z.B. um ihn mit print auszugeben) war noch umständlicher:

meinErfolg, meinWert = EEPLoadData(1)
print(meinWert)

Jetzt kann auf die zusätzlichen Variablen verzichtet werden:

print(Slot[1])

EEP stellt (ab Version 11, vorher gab es das gar nicht) 1000 Slots zur Verfügung. Daran ändert auch der „Zucker“ nichts.

Durch Schreiben von nil wird ein Slot gelöscht. Das war auch mit EEPSaveData so, und funktioniert auch genau wie bei normalen Lua-Tabellen. Das Auslesen eines nicht vorhandenen Slots ergibt wiederum nil. Das ist wieder wie bei normalen Lua-Tabellen, aber anders als bei EEPLoadData: Dort musste der erste Rückgabewert geprüft werden.

Code-Beispiel

Ich habe das Standard-Skript, das EEP bei einer neuen Anlage erzeugt, mal so umgeschrieben, dass der Zähler I, der laufend im Ereignisfenster ausgegeben wird, durch einen Slot ersetzt wird. Dieser fängt dann nicht bei jedem Neuladen des Skripts oder der Anlage bei 0 an zu zählen, sondern macht (sofern Zeile 3 auskommentiert wurde) beim letzten Wert weiter.

Slot = require("SlotSugar_BH2")

Slot[1]=0 -- nach dem ersten Durchlauf auskommentieren/löschen
clearlog()

print("Hey let's start, EEP Version is: ", EEPVer)

function EEPMain()
    print(Slot[1])
    Slot[1]=Slot[1]+1
    return 1
end
Der SlotSugar wird in diesem Skript gleich viermal verwendet: In Zeile 3 wird ein Wert (die 0) in Slot 1 gespeichert. In Zeile 9 wird dieser Wert wieder ausgelesen. In Zeile 10 wird der Wert nochmal ausgelesen, um eins erhöht und gleich wieder gespeichert.

Changelog

v0.1 vom 07.01.2017:
  • Einfacheres Speichern in und Laden aus den von EEP bereitgestellten Slots