Christophers Lua-Module

!!! Please ensure, that your contribution or question is placed into the relevant section !!!
Questions about rolling stock, for example, do not belong in "Questions about the Forum". Following is perhaps the right area where your question will be better looked after:
General questions to EEP , Splines, rolling stock, Structures in EEP, landscape elements, Signalling system and controlling, designers, Europe-wide EEP meetings , Gossip
Your cooperation to keep the forum clear is appreciated.
  • Hallo Christopher,


    tolle Idee von Dir, bin schon gespannt auf das nächste Kapitel...


    Gruß Frank

    EEP16 Expert Patch2, Modellkatalog, Modellexplorer, HomeNostruktor15, EEP-Zugexplorer,

    i9-9900K 8X 3.6 GHz, MSI Z390 Gaming Pro Carbon, 32GB DDR4,

    GTX1070-8G Gaming Grafikkarte

  • Kapitel 2. Debug — Einschaltbare Ausgaben zur Fehlersuche

    Motivation

    Wer programmiert, macht Fehler und sucht und sucht und sucht.


    Für Lua in EEP gibt es keinen Debugger, mit dem man die Verarbeitung anhalten, Variableninhalte überprüfen und verändern oder den Weg, wie diese Funktion erreicht wurde, anzeigen könnte. Als muss man zur Fehlersuche auf die alte Vorgehensweise zurückgreifen, an interessanten Stellen Ausgaben per print-Anweisung zu erstellen und im EEP-Ereignisfenster anschauen. So bin ich schon auf meinem ersten programmierbaren Taschenrechner unter BASIC vorgegangen.


    Also spickt man seinen Code mit print-Anweisungen, findet so Fehler, korrigiert sie und löscht die print-Anweisungen wieder, weil sie einem das EEP-Ereignisfenster verhageln. Nach einer Weile und bei der Suche nach dem nächsten Fehler, habe ich dann häufig gemerkt, dass ich genau die gelöschte print-Anweisung wieder brauche und habe sie erneut geschrieben. Und so weiter...


    Als ich dessen überdrüssig war, habe ich das Modul Debug erstellt. Die Prüfausgaben werden durch das Modul geleitet, aber nur ausgegeben, wenn die Ausgabe gewünscht ist. Dazu schreibe ich ins Anlagen-Skript, welche Debug-Ausgaben ich haben will, z.B. Debug.set("TrackInfo"). Der Name (hier "TrackInfo") ist zunächst Schall und Rauch. Wenn ich jetzt eine Debug-Ausgabe zum Thema "TrackInfo" haben will, so schreibe ich etwa Debug.print("TrackInfo", "track = "..track);. Das prüft, ob die Ausgabe für "TrackInfo" aktiviert ist und gibt den Text dann aus; wenn es nicht aktiviert ist, passiert nichts. So können die Debug-Ausgaben im Code stehen bleiben.


    Die Bereiche (oder Themen oder Kontexte), für die die Debug-Ausgaben separat ein- und ausgeschaltet werden können, habe ich Einheiten (units) genannt. Sie können jeweils mit dem Namen des Moduls übereinstimmen – und tatsächlich tun sie das bei mir auch – , müssen aber nicht. Ein Grund, anders vorzugehen, wäre ein Modul, das sich auch in die EEPMain einklinkt. Hier kann man gut zwei Einheiten definieren, eine für Ausgaben innerhalb von EEPMain, die natürlich häufig ausgegeben werden, und eine für den Rest.

    Konzept

    Ich habe mir also ein Modul mit folgenden Eigenschaften erstellt:

    • ein Speicher, für welche Einheiten Debug-Ausgaben aktiviert sind (damit ist das Modul zustandsbehaftet, stateful).
    • eine Funktion Debug.set(unit) zum Aktivieren von Debug-Ausgaben
    • eine Funktion Debug.unset(unit) zum Deaktivieren von Debug-Ausgaben
    • eine Funktion Debug.isSet(unit) zum Prüfen, ob Debug-Ausgaben aktiviert sind
    • eine Funktion print mit dem ersten Parameter unit und beliebig vielen weiteren Parametern, die die Aktivierung von unit prüft und ggf. die übrigen Parameter ausgibt
    • eine analoge Funktion printf, die die übrigen Parameter nimmt, um mit string.format eine Zeichenkette zu formatieren und diese ggf. auszugeben. (Ich habe sehr lange und viel im C programmiert und dort ist mir printf in Fleisch und Blut übergegangen; ich kann ohne nicht leben. Ob Ihr sie braucht, ist Eure Sache.)

    Details

    Der Speicher für die Aktivierungen ist bei mir eine Tabelle, in der die Einheiten (units) die Indizes (Schlüssel, englisch "keys") sind. Einen Wert brauche ich eigentlich gar nicht; formal braucht aber Lua einen Wert, also ist der Wert einfach true. Die Sematik ist: Wenn die gesuchte Einheit in der Tabelle vorhanden ist, ist die Debug-Ausgabe aktiviert, andernfalls nicht. Zunächst ist keine Einheit aktiviert, also:

    LUA Source Code: Debug_2.lua
    1. local units = {};


    Die Funktion set akzeptiert sogar einen zweiten, optionalen Parameter. Damit kann ich eine Einheit aktivieren mit Debug.set("Einheit") oder mit Debug.set("Einheit", true). Und ich kann sie deaktivieren mit Debug.set("Einheit", false). Wenn ich sie deaktiviere, soll sie aus dem Speicher verschwinden (minimaler Speicherbedarf). Dazu wird der Wert im Speicher auf nil gesetzt. Die Funktion set sieht dann so aus:

    LUA Source Code: Debug_2.lua
    1. local function set(unit, boolean)
    2. if boolean or boolean == nil then
    3. units[unit] = true;
    4. else
    5. units[unit] = nil;
    6. end;
    7. end;

    Noch eine kurze Erklärung zu if boolean or boolean == nil then: Das erste boolean (ohne Vergleich) ist immer true, wenn irgendetwas anderes als false oder nil drin steht. Ich will aber, dass ein fehlendes zweites Argument auch zur Aktivierung führt, dann ist boolean nil, daher der Ausdruck boolean == nil hinter dem or.


    Nachdem set eigentlich schon alles kann, auch das Deaktivieren, ist die Funktion unset reine "Convenience" und greift auf die Dienste von set zurück

    LUA Source Code: Debug_2.lua
    1. local function unset(unit)
    2. return set(unit, false);
    3. end;

    (Für maximale Unterstützung von "monkey patching" müsste man das anders machen; aber mir erscheint das übertrieben.)


    Die Funktion isSet soll immer true oder false zurückgeben:

    LUA Source Code: Debug_2.lua
    1. local function isSet(unit)
    2. return units[unit] or false;
    3. end;

    Ein return units[unit] würde nil zurückgeben, wenn die Einheit nicht aktiviert ist. Das wäre zwar nicht schlimm, aber m.E. auch nicht schön. Daher habe ich das or false angehängt. So kommt wirklich nur true oder false zurück.


    Die Funktion print würde zunächst mit dem Namen der globalen Funktion kollidieren, daher heißt sie erst einmal doPrint. Wenn ich die Funktion "exportiere", räume ich noch auf.

    Diese Funktion soll nun beliebig viele Argumente aufnehmen. Zum Glück gibt es dafür in Lua das Konstrukt .... Man kann sich ... als (Pseudo-) Variable vorstellen, in der die übrigen Argumente aufgefangen werden. Jetzt könnte ich diese Werte mit table.pack(...) in eine Tabelle übernehmen. Aber das brauche ich gar nicht; ich reiche sie einfach weiter.

    LUA Source Code: Debug_2.lua
    1. local function doPrint(unit, ...)
    2. if units[unit] then
    3. print(...);
    4. end;
    5. end;


    Für printf wird es noch ein bisschen trickreicher. Der Grund ist der, dass ich mich im Format-String doch ab und zu verschreibe. Dann erhalte ich eine Fehlermeldung, dass im Modul Debug etwas nicht stimmt. Na toll! :ad_1: Geholfen hätte mir, wenn ich die aufrufende Stelle kennen würde!

    Aber auch dafür gibt es eine Lösung: Ich fange einen eventuellen Fehler zunächst mit pcall ab und werfe ihn dann explizit, wobei ich verlange, dass die Fehlermeldung die Position der aufrufenden Stelle ausgeben soll. Das leistet der zweite Parameter von error.

    LUA Source Code: Debug_2.lua
    1. local function printf(unit, ...)
    2. if units[unit] then
    3. local success, text = pcall(string.format, ...);
    4. if success then
    5. print(text);
    6. else
    7. error(text, 2);
    8. end;
    9. end;
    10. end;


    Jetzt haben wir die Anforderungen aus dem Konzept erfüllt. Packen wir das Ganze in eine Tabelle, geben die Version des Moduls aus und liefern die Tabelle zurück. Das ist auch die Stelle, an der ich die Versionsnummer definiere, den vorläufigen Namen doPrint auf print ändere und meine Funktionen beschreibe, damit ich auch in Zukunft noch weiß, was die Funktionen leisten. Die Form der Beschreibung ist immer diesselbe:

    • Funktion mit Namen und Parametern
    • Bedeutung (Zweck, Leistung) der Funktion: Das fällt mal länger und mal kürzer aus.
    • Beschreibung der Parameter
    • Beschreibung der Rückgabewerte


    Das war ein erstes, einfaches Modul. Aber auch das hatte schon ein paar fortgeschrittenere Lua-Konstrukte.


    Bis bald

    Christopher

    PC: Intel i7-7700K; 64bit; 4,2 GHz; 16GB RAM; GeForce GTX 1080 (8 GB); Win 10; EEP 6, 13.2 Plugins 1+2, 14.1 (Dev), 15 (Dev); HomeNOS 14 (Dev)
    Laptop: Intel i5 3230M; 64bit; 2,6 GHz; 8GB RAM; GeForce GT740M (1 GB); Win 8.1; EEP 6, 13.2 Plugins 1+2; HomeNOS 13 (User)

    The post was edited 1 time, last by cetz: LUA -> Lua ().

  • Hallo Christopher,


    das ist eine Herangehensweise die mir sehr gefällt. Ich danke dir für die Mühe das so ausführlich darzustellen und werde sicher das eine oder andere übernehmen.


    Grüsse aus dem Harz

    Gerhard/Gonz

  • Diesen Beitrag musste ich in zwei Teile aufteilen, weil er sonst die maximale Länge überschreiten würde.

    Kapitel 3. Mutex — "Der nächste bitte!"

    Motivation

    Manchmal gibt es Bereiche in einer Anlage, in denen sich nur einer aufhalten darf. Das kann ein Zug sein, ein Auto oder was auch immer. Wenn ein zweiter in den Bereich will, muss er warten, bis der Bereich wieder frei ist.


    Ein typischer Anwendungsfall ist ein eingleisiger Abschnitt in einer zweigleisigen Strecke. Oder auch nur eine Gleisannäherung, die an dieser Stelle keinen Gegenverkehr zulässt. Oder eine Zusammenführung mehrerer Strecken. Ich verlange in diesem Modul weder, dass dieser Abschnitt nur von genau 2 Stellen aus erreichbar ist, noch dass die Gleise sich tatsächlich zu einem Gleis vereinen, noch dass es überhaupt um Gleise geht.


    Einen Anwendungsfall habe ich in meiner Anlage Weilburg: Der Schiffstunnel und die anschließende Schleusentreppe bilden solch einen Bereich. Die Paddler auf der Lahn fahren immer nur in Fließrichtung, unten angekommen werden sie von einer virtuellen Gleisverbindung wieder nach oben katapultiert. In Weilburg legt ein Ausflugsschiff ab, das auch durch den Schiffstunnel fährt, unterhalb davon wendet, und auf der Rückfahrt wieder durch den Schiffstunnel fährt. Ich habe also drei auslösende Stellen: Im Verlauf der Paddlerstrecke, am Schiffsanleger, wenn das Ausflugsschiff losfahren will, und unterhalb der Schleusentreppe für die Rückfahrt des Ausflugsschiffs.


    Generell findet der Ablauf so statt: Ein Zug (im EEP-Sinne, das kann auch ein Paddelboot etc. sein) fährt über einen Kontaktpunkt und verlangt damit Zugang in den geschützten Bereich. Der Zugang wird meistens dadurch gewährt, dass ein Signal, das der Zug angefordert hat, auf "freie Fahrt" springt. Wenn der Bereich frei ist, erfolgt die Aktion sofort. Wenn er besetzt ist, wird die Anforderung abgespeichert. Nach Passieren des Signals stellt der Zug es wieder auf "Halt". Sollten weitere Züge den Bereich betreten wollen, werden auch ihre Anforderungen abgespeichert. Am Ende des Bereichs gibt es einen weiteren Kontaktpunkt, der signalisiert, dass der Bereich nun wieder frei ist. Wenn ein weiterer Zug auf den Bereich wartet, wird nun die Aktion ausgelöst, die er angefordert hatte (typischerweise eben wieder ein Signal).


    In der Programmierung tritt solch eine Anforderung häufiger auf. Man spricht von "gegenseitigem Ausschluss", auf englisch "mutual exclusive". Und weil Akronyme ja so "sexy" sind, hat sich dafür der Begriff "Mutex" eingebürgert. Daher der Name des Moduls.

    Konzept

    Das Modul braucht also ein Gedächtnis, ob der Bereich belegt ist, und ggf. welche Aktionen noch nicht ausgeführt werden durften, sondern warten müssen. Es ist also wieder stateful (zustandsbehaftet).


    Natürlich soll das Modul mit beliebig vielen Bereichen umgehen können, nicht nur mit einem.


    Es werden zwei Funktionen benötigt:

    • request verlangt den Eintritt in den geschützten Bereich.
      Dabei wird der Bereich über einen eindeutigen Namen identifiziert. Die Aktion wird entweder über zwei Ganzzahlen beschrieben, die dann eine Signalnummer und den gewünschten Zustand des Signals enthalten, oder über eine Zeichenkette, die einen Lua-Schnipsel enthält.
      Beispiele:
      • Mutex.request("Schleusentreppe", 42, 1): Stellt das Signal 42 auf Stellung 1, wenn der Bereich frei ist / frei wird.
      • Mutex.request("Schleusentreppe", "EEPSetSignal(42, 1)"): macht dasselbe. Hier sollte aber klar werden, dass man auch ganz andere Aktionen auslösen kann, indem man etwas anderes als zweites Argument übergibt.
    • release gibt den Bereich wieder frei. Einziger Parameter ist der Name des Bereichs, wie er bei request verwendet wurde.
      Beispiel:
      • Mutex.release("Schleusentreppe")

    Zur Überprüfung des Bereichs habe ich noch eine dritte Funktion geschrieben. Diese hänge ich an ein Signal, das ich über das GBS schalten kann. So sehe ich durch Schalten des Signals, ob der Bereich frei oder besetzt ist, und ggf. wie viele Aktionen auf ihre Ausführung warten.

    • show zeigt den Zustand des Bereich an. Einziger Parameter ist wieder der Name des Bereichs.
      Beispiel:
      • Mutex.show("Schleusentreppe")

    Die Speicherung läuft so:

    • Ein freier Mutex wird gar nicht gespeichert.
    • Ein belegter Mutex ohne wartende Anforderung wird als leere Tabelle gespeichert.
    • Wartende Anforderungen werden in dieser Tabelle gespeichert; sie ist dann eben nicht leer.

    Wenn die Speicherung ordentlich arbeitet, funktioniert das auch über das Schließen und Neustarten der Anlage hinweg.

    Details

    Das Modul Mutex fängt so an:

    LUA Source Code: Mutex_3.lua
    1. local Debug = require "Debug_2";
    2. local P = require "Persistence_3";
    3. local loadstring = loadstring or load;

    Die erste Zeile lädt das Modul Debug für Ausgaben zur Fehlersuche. Das kennt Ihr bereits.

    Die zweite Zeile lädt ein Modul zur Speicherung. Es basiert auf EEPSaveData und EEPLoadData, kann aber auch mit Tabellen umgehen. Das Modul werde ich Euch beim nächsten Mal vorstellen. Für heute nur so viel: Das Modul gibt eine Tabelle zurück (hier als P entgegengenommen), die die spezielle Eigenschaft hat, alle ihre Felder zu speichern, bzw. bei Zugriff auf ein Feld den entsprechenden Wert aus der Speicherung zu holen.

    P.feld = wert speichert den Wert.

    x = P.feld holt den Wert aus der Speicherung zurück.

    Die dritte Zeile ist ein Zugeständnis an meine "Entwicklungsumgebung": Ich entwickele meistens zunächst außerhalb von EEP. Dazu brauche ich einen Lua-Editor (da gibt es viele) und einen integrierten Debugger (da wird's schwierig). Ich habe mich für die kompakte Entwicklungsumgebung LuaForWindows entschieden, die den Editor SciTe und Lua5.1 enthält. Damit habe ich aber leider nur Lua in der Version 5.1, während EEP die Version 5.3 enthält. Ein Unterschied zwischen den Versionen ist, dass Lua 5.1 die Funktion loadstring enthält, um eine Zeichenkette in ausführbaren Code umzuwandeln (zu kompilieren). In Lua 5.3 gibt es diese Funktion nicht mehr; ihre Funktionalität wurde in load integriert.

    Die Zeile local loadstring = loadstring or load; macht also folgendes: Merke Dir in der lokalen Variablen loadstring den Wert von der globalen Variablen loadstring, wenn er existiert. Und wenn nicht, speichere dort den Wert von load. Mit "Wert" meine ich eine Funktion, ohne sie jedoch an dieser Stelle auszuführen.


    Zunächst brauche ich drei lokale "Hilfs"-Funktionen: get holt Mutex-Informationen aus dem Speicher, put legt geänderte Informationen dort ab und execute übersetzt eine Zeichenkette in ausführbaren Code und führt ihn aus, wenn er übersetzt werden konnte.


    Hier der Code von get:

    LUA Source Code: Mutex_3.lua
    1. local function get(mutexName)
    2. if type(mutexName) ~= "string" then
    3. error("mutexName must be a string, but actually is a " .. type(mutexName), 2);
    4. end;
    5. return P["Mutex$"..mutexName];
    6. end;

    Die kleine Typprüfung des Arguments dürfte ohne weitere Erläuterungen verständlich sein.


    Der Code von put:

    LUA Source Code: Mutex_3.lua
    1. local function put(mutexName, mutex)
    2. P["Mutex$"..mutexName] = mutex;
    3. end;


    Und hier execute:

    In Zeile 4 wird die Zeichenkette übersetzt (kompiliert). Hier verwende ich das lokale loadstring, das nach obiger Zuweisung immer existiert. Wenn die Übersetzung ohne Fehler möglich war, wird der ausführbare Code zurückgegeben. Andernfalls enthält der erste Rückgabewert nil und der zweite eine Fehlermeldung.


    In Zeile 8 wird (ganz vorsichtig) versucht, den kompilierten Code auszuführen. Argumente werden keine übergeben. Eventuelle Rückgabewerte werden nicht entgegengenommen. Nur wenn die Ausführung ohne Fehler ablief, wird true zurückgegeben.


    Die Standardfunktion error (Zeilen 6 und 10) gibt den übergebenen Text aus und bricht die Abarbeitung ab. "Hinter" diesem Aufruf geht es also nie weiter.

    PC: Intel i7-7700K; 64bit; 4,2 GHz; 16GB RAM; GeForce GTX 1080 (8 GB); Win 10; EEP 6, 13.2 Plugins 1+2, 14.1 (Dev), 15 (Dev); HomeNOS 14 (Dev)
    Laptop: Intel i5 3230M; 64bit; 2,6 GHz; 8GB RAM; GeForce GT740M (1 GB); Win 8.1; EEP 6, 13.2 Plugins 1+2; HomeNOS 13 (User)

    The post was edited 1 time, last by cetz: Einrückungen korrigiert ().

  • Fortsetzung:



    Die Funktion request sieht nun so aus:

    In Zeile 2 hole ich mir die Informationen zu dem angefordeten Mutex.


    In den Zeilen 4 bis 10 analysiere ich die Parameter: Aus zwei Zahlen wird eine Zeichenkette mit Aufrufen von EEPSetSignal und EEPSetSwitch gemacht. Die übergebene ID ist entweder die eines Signals oder die einer Weiche. Es wird also genau einer der beiden Aufrufe funktionieren. Der andere tut einfach nichts; er wirft auch keinen Fehler. Wenn der zweite Parameter bereits eine Zeichenkette enthält, ist nichts weiter zu tun. Andere Datentypen akzeptiert die Funktion nicht.


    In Zeile 11 wird geprüft, ob der Mutex frei ist.

    Wenn nein, wird die Aktion als neues, letztes Element in die Tabelle aufgenommen (Zeile 12) und aus der Variablen action entfernt (Zeile 14).

    Wenn der Mutex frei ist, wird eine leere Tabelle angelegt. Diese markiert, dass der Mutex jetzt belegt ist.


    In Zeile 19 wird der aktuelle Zustand des Mutex gespeichert.

    Und in Zeile 20 wird die Aktion, wenn sie denn noch in der Variablen steht, ausgeführt. Die Prüfung auf nil steht in der Funktion execute.


    Die Funktion release sieht so aus:

    Es wird der Mutex aus der Speicherung geholt.

    Wenn er gar nicht vorhanden war, war er nicht belegt. Das hätte eigentlich nicht vorkommen sollen. In diesem Fall (Zeile 12) wird nur eine Debug-Ausgabe erzeugt. Ob sie im EEP-Ereignisfenster erscheint, hängt von der Debug-Einstellung für die Einheit "Mutex" ab.

    Wenn der Mutex aber aus der Speicherung geholt werden konnte, habe ich eine Tabelle zurückbekommen. Aus dieser Tabelle hole ich den ersten Wert (Zeile 5) und lösche ihn in der Tabelle. Dieser Wert ist die am längsten wartende Anforderung. Falls es keinen solchen Wert gibt, war der Mutex zwar belegt, aber ohne wartende Anforderungen.

    Der neue Zustand des Mutex wird gespeichert (Zeile 9).

    Falls es eine nächste Anforderung gab, wird diese ausgeführt (Zeile 10).


    Die Funktion show sieht so aus:

    Hier ist nur noch der (unäre) Operator # erklärungsbedürftig. Er ermittelt die Anzahl der Einträge in der Tabelle. Genauer gesagt: Er sucht ausgehend von 1 den höchsten Index n, so dass Tabelle[n] einen Wert hat, Tabelle[n+1] aber nicht. Er kann also nicht mit Lücken in der Indizierung umgehen und zählt auch keine nicht-numerischen Einträge mit. Beides ist in diesem Modul aber irrelevant.


    Jetzt können wir das Ganze zusammensetzen, die exportierten Funktionen beschreiben, die Versionsnummer ausgeben und die Mutex-Tabelle zurückgeben:



    Geht das denn alles gut?

    Das ist eine berechtigte Frage. Man muss sicher sein, dass es keine Konstellation gibt, in der zwei Anforderungen parallel erfüllt werden. So etwas nennt man eine "race condition".


    Ich bin mir ziemlich sicher, dass es gut geht. Das schlimmste, was passieren kann, ist, dass zwei Züge quasi zeitgleich eine Anforderung für denselben Bereich stellen. Lua läuft in EEP aber immer sequentiell ab. Egal, wie schnell die Anforderungen nach einander kommen, der Lua-Code für die eine Anforderung wird vor der anderen ausgeführt, und zwar vollständig. Am Ende der ersten Anforderung ist der Bereich als besetzt markiert, und die zweite Anforderung muss warten, bis der Bereich wieder frei ist.


    Was man natürlich nicht tun darf, ist, einen Zug im geschützten Bereich von den Schienen zu nehmen. Dann fehlt der Aufruf von release und der Bereich bleibt gesperrt. In diesem Fall kann man immer noch einmalig den passenden release-Aufruf ins Anlage-Skript schreiben, das Skript neu laden, den Aufruf wieder entfernen und das Skript nochmal neu laden.



    Jetzt muss ich Euch wohl als nächstes mein Modul "Persistence" vorstellen.


    Bis denne

    Christopher

    PC: Intel i7-7700K; 64bit; 4,2 GHz; 16GB RAM; GeForce GTX 1080 (8 GB); Win 10; EEP 6, 13.2 Plugins 1+2, 14.1 (Dev), 15 (Dev); HomeNOS 14 (Dev)
    Laptop: Intel i5 3230M; 64bit; 2,6 GHz; 8GB RAM; GeForce GT740M (1 GB); Win 8.1; EEP 6, 13.2 Plugins 1+2; HomeNOS 13 (User)

  • Hallo Christopher,

    ersteinmal vielen Dank für Deine Mühe - echt toll !!

    Das wirkt unheimlich professionell. Als wenn Du das jeden Tag unterrichten würdest !!


    Ich habe versucht, Deine Ausführungen für den eigenen Bedarf zu kopieren. Was den reinen Text angeht.....kein Problem.

    Nur mit den Programm-Zeilen habe ich hinsichtlich der Formatierung und Farbe Probleme.

    Hab's mit OpenOffice und Word probiert.

    Mit dem FoxitReader - also pdf - geht's gar nicht.

    Hat jemand einen Tip ?

    Viele Grüße aus Berlin :aq_1:

    Manfred


    "Es kommt nicht darauf an, mit dem Kopf durch die Wand zu gehen, sondern mit den Augen die Tür zu finden." :ae_1: (Werner von Siemens)


    EEP X Edition: Die Schiefe Ebene, EEP 13.2.2 Expert, EEP 14.1 Expert PlugIn 1, EEP 15.1 Expert

    Laptop MSI GT72 6QD Dominator G, Win 10 Home, Intel Core i7-6700 HQ 2,60 GHz, 16 GB RAM, 64-Bit-Betriebssystem, NVIDIA GTX970M G-Sync

  • Ein sehr interessanter Ansatz. Durch das Beispiel mit der Engstelle hat man nur zuerst den Eindruck, Du würdest ein Kanone erschaffen, um auf einen Spatzen zu schießen. Ich habe es zweimal lesen müssen, bis ich gemerkt habe, was das Modul eigentlich ist: Eine universelle Warteschlangenverwaltung für beliebige Aufgaben, die nacheinander abgearbeitet werden sollen. Die "Verkehrsüberwachung" ist da nur eine von vielen Einsatzmöglichkeiten.


    ich kann mir vorstellen, das für die Ausfahrt aus meinem virtuellen Schattenbahnhof zu verwenden, dann da gibt es eine Warteschlange von angeforderten Zügen. Was ich auf jeden Fall daraus mitnehme ist die eigentlich banale aber gern übersehene Tatsache , dass einfache Wartesituationen bei EEP nicht unbedingt ständig durch EEPMain() abgefragt werden müssen, sondern dass es im Prinzip ausreicht, wenn das worauf gewartet wird, eine Rückmeldung gibt, dass es damit fertig ist.


    Das "Persistance"-Modul klingt vielversprechend, wenn es das ist, was es zu sein scheint: eine Speichermöglichkeit für beliebige Tabellen, ohne sich um deren Struktur kümmern zu müssen.

  • Hat jemand einen Tip ?

    Hallo :)


    um den Thread von cetz nicht unnötig aufzublähen, habe ich die Diskussion darüber in den eigenen Thread LUA-Code drucken (als PDF) verschoben.


    Gruß Ingo

    Win 10x64 Professional, Gigabyte B360M AORUS, Intel Core i7-8700K, 32GB RAM, NVIDIA GeForce GTX 1050 Ti 4GB, 1920x1200 auf 26" TFT

    Windows Firewall und Defender
    EEP 15.1 Expert Patch 2, Plugins: 1

    EEP 16 Expert Patch 2


    alt:
    Win 7x64 Professional SP1, Intel Core i5-3570, 16GB RAM, nVIDIA GeForce GT630, 2GB, 1280x1024 auf 19" TFT

    Windows Firewall und Defender (MSE)

    EEP6; EEP8-14; EEP 15.1 Expert Patch 2, Plugins: 1

  • Hallo zusammen, bin auch sehr interessiert an Deinem Lua Unterricht :an_1:

    Wäre es Dir möglich eine Beispielanlage zu erstellen. So klein wie möglich gehalten um zb. den Schienenverkehr zu überwachen. Mir , der sich nicht so gut mit Programmierung auskennt, würde das sehr helfen dieses zu verstehen. Und ich denke mal da bin ich nicht der einzige :ae_1:

    Ich Danke Dir schonmal für Deine Mühen und werde Deine weiteren Erläuterungen mit großem Interesse weiter verfolgen.

    mfg Micha :aq_1:


    Tiachi x399

    AMD Threadripper X1950 3,40 Ghz

    64GB Ram

    Geforce GTX 1080 8GB DDR5

    EEP 14 Patch 2

    EEP 15.1 Patch 1,2

    EEP 16 Patch 2 :ak_1:


    Windows-Spezifikationen

    Edition Windows 10 Pro

    Version 1903

    BSBuild 18362.295

  • Hallo Micha ( hurricane65 ),

    Wäre es Dir möglich eine Beispielanlage zu erstellen.

    natürlich wäre mir das möglich. :af_1: Aber ich werde es nicht tun. :ag_1: Die Gründe sind:

    • Ich möchte Eure Kreativität anregen, was mit Lua möglich ist, und Euch ermutigen, Eure eigenen Lua-Wege zu gehen. Ein paar Informationen über guten und weniger guten Programmierstil liefere ich ja mit.
    • Ich möchte keine Copy&Paste-Vorlagen liefern, sondern Euch ermutigen, das in Lua zu realisieren, was Ihr in Euren Anlagen braucht. Aus diesem Grund gibt es auch nur die Code-Schnipsel und keinen kompletten Lua-Code.
    • Die Module sind weitestgehend Anlagen-unabhängig. Man kann sie für alles Mögliche verwenden. Das ist zum Beispiel klioli beim zweiten Lesen bewusst geworden.
    • Ich traue Euch allen zu, eine Anlage mit einem eingleisigen Abschnitt oder mit einer 5fachen Zusammenführung von Strecken oder was auch immer zu bauen, und daran den Code zu testen.

    In diesem Sinne: Sorry

    Christopher

    PC: Intel i7-7700K; 64bit; 4,2 GHz; 16GB RAM; GeForce GTX 1080 (8 GB); Win 10; EEP 6, 13.2 Plugins 1+2, 14.1 (Dev), 15 (Dev); HomeNOS 14 (Dev)
    Laptop: Intel i5 3230M; 64bit; 2,6 GHz; 8GB RAM; GeForce GT740M (1 GB); Win 8.1; EEP 6, 13.2 Plugins 1+2; HomeNOS 13 (User)

  • Hallo Christopher,


    Dein Vorhaben ist sehr toll.

    Aber hast Du auch vor, diesen ganzen Leitfaden oder wie auch immer Du das nennen möchtest - am Ende in einem gesammelten Werk zur Verfügung zu stellen?

    Denn, es ist sehr mühsam die interessanten Inhalte zu kopieren und sie dann in einem Werk zusammen zu haben.

    Ich bin kompletter LUA - Anfänger und würde diese Sprache gerne lernen!

    Mfg Bernhard

    RAM = 32 Gb/

    Grakik = NVIDIA GEFORCE GTX 1050Ti 4Gb

    SSD 1 = 480Gb Windows 10 Pro 64 Bit/Version 1909/ Build 18363.476

    SSD 2 = 480Gb EEP und andere EEP - Programme/EEP 6.1 nur zum konvertieren/EEP 12 Expert / Update 1 / PlugIn 1/EEP13 mit allen Plugin und Patches/EEP14 mit allen Plugin und Patches/EEP15.1 mit allen Patches und PlugIn 1/EEP16 mit Patch 1

    Tauschmanager/Modellkatalog/Modellkonverter 1.4/Bilder-Scanner/Modell-Explorer/Ressourcen-Switch


  • Hallo Bernhard,

    Aber hast Du auch vor, diesen ganzen Leitfaden oder wie auch immer Du das nennen möchtest - am Ende in einem gesammelten Werk zur Verfügung zu stellen?

    Ich bin kompletter LUA - Anfänger und würde diese Sprache gerne lernen!

    Die Sprache lehren will ich definitiv nicht. Dazu sind Götz' Beiträge u.a. in Funktionsargumente und return-Werte wesentlich besser geeignet.

    In diesem Thread setze ich schon einiges an Lua-Verständnis voraus und erläutere meine spezielle Art, Lua zu verwenden, nur ganz kurz. Insofern richtet sich das eher an "fortgeschrittene Anfänger".


    Und zu Deiner Frage nach einem Sammelwerk hatte ich bereits geantwortet:

    Nee, das plane ich eigentlich nicht. Soooo gerne schreibe ich nämlich auch wieder nicht. Und als PDF wäre es mir zu sehr "Gesetz". Ich möchte Euch viel lieber zeigen, wie ich mich durchs Gestrüpp der Programmierung wühle. Eure Vorgehensweisen sind vielleicht andere. Lua lässt einem da auch sehr viele Freiheiten.


    Gruß

    Christopher

    PC: Intel i7-7700K; 64bit; 4,2 GHz; 16GB RAM; GeForce GTX 1080 (8 GB); Win 10; EEP 6, 13.2 Plugins 1+2, 14.1 (Dev), 15 (Dev); HomeNOS 14 (Dev)
    Laptop: Intel i5 3230M; 64bit; 2,6 GHz; 8GB RAM; GeForce GT740M (1 GB); Win 8.1; EEP 6, 13.2 Plugins 1+2; HomeNOS 13 (User)

  • Hallo Christopher,

    Du machst hier ja eine Doktorarbeit. Das hilft sicher einigen bei der Umsetzung für eine eigene Anlage weiter. Mir ganz bestimmt.

    Besten Dank dafür und weiter so. Super.

    Schönen Abend

    wegu1

    Hardware:

    Lenovo P330 - Intel Core i7-8700K - 32 GB RAM - Win 10 Prof 64 Bit

    Grafikkarte NVIDIA Quadro P2000 5GB

    EEP-Software:

    EEP 7-15 Expert - Home Nostruktor 14+15

  • Die Sprache lehren will ich definitiv nicht.

    Insofern richtet sich das eher an "fortgeschrittene Anfänger".

    Hallo.....diese Hinweise hätten eher kommen sollen,,,,,:ac_1:

    Viele Grüße aus Berlin :aq_1:

    Manfred


    "Es kommt nicht darauf an, mit dem Kopf durch die Wand zu gehen, sondern mit den Augen die Tür zu finden." :ae_1: (Werner von Siemens)


    EEP X Edition: Die Schiefe Ebene, EEP 13.2.2 Expert, EEP 14.1 Expert PlugIn 1, EEP 15.1 Expert

    Laptop MSI GT72 6QD Dominator G, Win 10 Home, Intel Core i7-6700 HQ 2,60 GHz, 16 GB RAM, 64-Bit-Betriebssystem, NVIDIA GTX970M G-Sync

  • Hallo.....diese Hinweise hätten eher kommen sollen

    Hallo,

    ich finde das ziemlich unverschämt, mühevolle Arbeit nur durch eigenes Unvermögen mit so einem Satz zu negieren...

    Gruß Frank

    EEP16 Expert Patch2, Modellkatalog, Modellexplorer, HomeNostruktor15, EEP-Zugexplorer,

    i9-9900K 8X 3.6 GHz, MSI Z390 Gaming Pro Carbon, 32GB DDR4,

    GTX1070-8G Gaming Grafikkarte

  • Wenn sich jemand mit LUA nicht so gut auskennet (wie ich), ist das kein Unvermögen. Ich finde Deine Bemerkung ziemllich Dumm.


    Peter

    Hallo,

    ich finde das ziemlich unverschämt, mühevolle Arbeit nur durch eigenes Unvermögen mit so einem Satz zu negieren...

    Gruß Frank

    Wenn sich jemand mit LUA nicht so gut auskennet (wie ich), ist das kein Unvermögen. Ich finde Deine Bemerkung ziemllich Dumm.


    Peter

    Betriebsystemname: Microsoft Windows 10 Pro Education

    Prozessor: AMD Ryzen 5 1600 Six-Core Processor, 3200 MHz, 6 Kern(e), 12 logische(r) Prozessor(en)

    PC:RAM 16 GB

    Grafik Karte: Name NVIDIA GeForce GTX 1060 6GB


    EEP6 mit allen Plugins und Patches
    EEP7 bis13 mit allen Patches und Plugins

    EEP 15 und EEP 16
    Modelkonverter
    PlanEx 3.1
    Home-Nostruktor 13.0
    Modellkatalog
    Bodentextur Tool



  • ich finde das ziemlich unverschämt, mühevolle Arbeit nur durch eigenes Unvermögen mit so einem Satz zu negieren...


    Gruß Frank

    Hallo Frank.......wenn Du Dir die Mühe gemacht hättest, weiter vorne zu lesen,wo ich Christopher ausdrücklich für seine Mühe und die tolle Arbeit gelobt und mich bedankt habe, dann wäre Dir vielleicht in den Sinn gekommen, das der Beitrag


    Hallo.....diese Hinweise hätten eher kommen sollen,,,,,:ac_1:

    nicht unverschämt gemeint sein kann sondern eher meint "kein Wunder warum ich nur Bahnhof verstehe".

    Du magst vielleicht recht haben, das ich mich als absoluter Neuling mit LUA schwer tue, aber Unvermögen würde ich das nicht nennen wollen. Allerdings darfst Du Dir - was den Ausdruck Unvermögen an geht - hinsichtlich Deines Vorwurfs UNVERSCHÄMT an die eigene Nase fassen :ag_1:

    Viele Grüße aus Berlin :aq_1:

    Manfred


    "Es kommt nicht darauf an, mit dem Kopf durch die Wand zu gehen, sondern mit den Augen die Tür zu finden." :ae_1: (Werner von Siemens)


    EEP X Edition: Die Schiefe Ebene, EEP 13.2.2 Expert, EEP 14.1 Expert PlugIn 1, EEP 15.1 Expert

    Laptop MSI GT72 6QD Dominator G, Win 10 Home, Intel Core i7-6700 HQ 2,60 GHz, 16 GB RAM, 64-Bit-Betriebssystem, NVIDIA GTX970M G-Sync

  • Hallo.....diese Hinweise hätten eher kommen sollen,,,,,

    Hallo Christopher.......ich hoffe Du hast mich richtig verstanden !!:aa_1:

    Viele Grüße aus Berlin :aq_1:

    Manfred


    "Es kommt nicht darauf an, mit dem Kopf durch die Wand zu gehen, sondern mit den Augen die Tür zu finden." :ae_1: (Werner von Siemens)


    EEP X Edition: Die Schiefe Ebene, EEP 13.2.2 Expert, EEP 14.1 Expert PlugIn 1, EEP 15.1 Expert

    Laptop MSI GT72 6QD Dominator G, Win 10 Home, Intel Core i7-6700 HQ 2,60 GHz, 16 GB RAM, 64-Bit-Betriebssystem, NVIDIA GTX970M G-Sync

  • diese Hinweise hätten eher kommen sollen,

    Ich habe da mal einen ganz allgemeinen Tipp zum Erlernen von Lua:


    Erläuterungen, denen man nicht folgen kann, sind noch nicht für einen geeignet. Ganz grundsätzlich nicht. Es nützt auch überhaupt nichts, wenn man sich diese Erläuterungen dann merkt, abheftet etc. Man kann aus ihnen nichts lernen.


    Lehrreich sind immer genau die Lektionen, die du verstehst. Und besonders gut sind die, bei denen du neue Erkenntnisse hast. Gleich beim ersten Lesen. Die musst du ein zweites und ein drittes Mal lesen. Aufmerksam. Und dann die Erkenntnisse benutzen. Konzentriere dich einzig auf die Lektionen, denen du folgen kannst. Das werden ganz von selbst immer mehr.


    Niemand kann dir vorweg sagen, welche Lektionen für dich geeignet sind und welche nicht. Weil das nichts damit zu tun hat, ob man Anfänger ist oder nicht. Es ist wie beim Puzzle: schieb alle Teile beiseite außer denen, die du einsetzen kannst. Welche der einzelne Mensch jeweils als passend erkennt und einsetzen kann, ist individuell verschieden. Bis auf die eindeutigen Eck- und Randstücke. Aber die erkennt man auch auf Anhieb. In den Lua Tutorials genauso wie beim Puzzle. Ohne, dass es jemand dran schreiben muss.



    Das gilt selbstverständlich nicht nur für dich. Sondern für alle, die etwas lernen wollen. Also auch für mich. Für Cetz. Für Benny ... Jeder erweitert sein Wissen mithilfe der Lektionen, die er versteht. Die anderen schiebt er - wie unpassende Puzzleteile - einfach zur Seite. Und man muss jede Lektion - wie Puzzleteile - einmal selbst anschauen um zu sehen ob sie schon irgendwo reinpassen oder nicht.

    "I think that there are more people that are bad than there are good.
    And if you're good you live forever and if you're bad, you die when you die."

    - child 9 -