User Tools

Site Tools


de:doc:lua_start

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
de:doc:lua_start [2013/11/05 00:07] wsauerde:doc:lua_start [2014/11/23 21:26] (current) wsauer
Line 1: Line 1:
-====== Erstelle Deine eigenen OOBD Skripte ====== 
- 
-Die "nachladbare Intelligenz" von OOBD ist als Lua Skript realisiert. Was das bedeutet und wie es arbeitet, wird hier beschrieben. 
- 
- 
 ===== Was ist Lua? ===== ===== Was ist Lua? =====
  
Line 15: Line 10:
  
  
-===== Die Lua - OOBD Schnittstelle ===== 
- 
-Wenn Lua innerhalb eines anderen Programms ohne weitere Hilfestellung gestartet wird, reagiert es wie eine Black Box: Es gibt keine Verbindung vom inneren zum äußeren Programm, keinerlei Eingabemöglichkeiten und keine Rückmeldungen an das aufrufende Programm. Das ist offensichtlich nicht besonders sinnvoll. Deshalb müssen, wenn Lua in ein Programm eingebaut wird, einige Schnittstellen erstellt werden, die Lua mit dem Host kommunizieren lassen.  
- 
-Aus der Sicht von Lua arbeiten diese Schnittstellen als normale Lua Funktionsaufrufe. Aber wenn eine solche Funktion aufgerufen wird, verzweigt die Ausführung des Programms in das äußere Programm, macht dort irgendwas und kehrt letztlich von dieser Funktion zurück zu Lua. 
- 
-Im Moment unterstützt OOBD zwei Arten von Schnittstellen für Lua: Einige die Menue's erstellen und andere die die Kommunikation über die serielle Schnittstelle ermöglichen. 
- 
-**Wichtig** Um während der Fehlersuche die festeingebauten erweiterten Lua Funktionen gegen selbsterstellten Kode austauschen zu können, werden normalerweise die festeingebauten Lua Funktionen zuerst einigen Lua Variablen zugewiesen. Diese werden dann später in dem Skript benutzt. 
- 
-  local serFlush =serFlushCall 
-  serflush() 
-   
-Mit dieser Benamungskonvention enden die festeingebauten erweiterten Funktionen mit xxx"Call", während die Aufrufe Dieser nachher ohne "Call" getätigt werden. 
- 
-Bitte behalte das im Gedächtnis, wenn Du dieses Dokument liest oder Deinen eigenen Kode schreibst. 
- 
-Aber bevor wir einen Blick in den Programmablauf werfen, müssen wir verstehen wie der Lua Compiler (Luac) arbeitet und was das für die Programminitialisierung bedeutet: 
- 
-==== Kompilierung und Aufstartkode ==== 
- 
-Um Speicherplatz zu sparen und die Aufstartzeit zu reduzieren, wird der Lua Interpretierer innerhalb OOBD nicht mit dem Quellcode ,der dann erst einmal kompiliert werden muss bevor er ausgeführt werden kann, versorgt. Stattdessen wird der fertig kompilierte Lua Programmkode eingebunden.  
- 
-Der Kompilierungsprozess funktioniert folgendermassen: Der Lua Kompilierer (Ein Komandozeilenprogrammaufruf genannt luac) übersetzt den oder die Quellkode(s), mit der Endung .lua, in eine einzelne Datei. Diese eine Datei hat nun die Endung .lbc und beinhaltet das übersetzte Programm.  
- 
-Der Kommandozeileneintrag sieht dann folgendermaßen aus: 
- 
-  luac -o Ausgabedateiname.lbc Quellkodename1.lua Quellkodename2.lua ... 
-   
-   
-In dem OOBD Quellkode Sammelverzeichnis sind einige Beispiele abgelegt, die zeigen wie es gemacht wird.   
- 
-Wenn Du nun Deinen eigenen Übersetzungsprozess beginnen möchtest, gibt es zwei Dinge die Du wissen musst, da luac ein wenig anders arbeitet als ein gewöhnlicher Kompilierer. 
- 
-  - luac beurteilt **nicht** einige require() oder doFile() statements, sodaß diese Dateien nicht in der Ausgabedatei wiedergefunden werden können. Alle benötigten Dateien müssen stattdessen als Eingabedateien eingebunden werden, um in der Ausgabedatei enthalten zu sein. 
-  - Auch die Reihenfolge der Eingabedateien ist wichtig: Luac bindet diese Dateien zusammen, in der Reihenfolge in der sie angefügt werden. In dieser Reihenfolge werden sie später auch ausgeführt. Hierbei musst Du darauf achten, das Variablen und Funktionen deklariert werden müssen **bevor** sie das erste Mal benutzt werden. Wenn das nicht passiert, bekommst Du eine Schutzverletzung. 
- 
-Wegen des zweiten genannten Punktes, sollte die Lua Funktion die die kompletten Lua Strukturen initialisiert, das letzte und einzige Kommando sein, das in der letzen Datei steht die luac als Eingabedatei dient. 
- 
-=== Das Start("","") Kommando === 
- 
-Um der OOBD Anwendung die Möglichkeit zu geben, neu initialisiert zu werden (wie z.B nach einem Verbindungsabbruch), 
-wird der Name einer solchen Initialisierungsfunktion mit **Start("","")** bestimmt. Jedes OOBD Programm muss diese Funktion enthalten. Stelle also sicher, das Dein Programm dies enthält und das Deine Start() Funktion allen notwendigen Initialisierungskode enthält. 
-   
-==== Die Menu Kommando's ==== 
-OOBD benutzt nur drei Funktionen um die Menue Strukturen zu realisieren. Deshalb lass uns zuerst einen Blick in einen Beispielkode werfen: 
- 
-<code lua> 
-function Start(oldvalue,id) 
- identifyOOBDInterface() 
- setSendID("$7E8") -- setzt nicht UDS kompatible Sender (=Antwort) Adresse für die OOBD firmware 
- openPage("OOBD-ME Main") 
- addElement("Sensor Data >", "createCMD01Menu",">>>",0x1, "") 
-        addElement("Snapshot Data >", "createCMD02Menu",">>>",0x1, "") 
-        addElement("Dynamic Menu3 >", "createCMD03Menu",">>>",0x1, "") 
- addElement("Trouble Codes", "showdtcs","-",0x1, "") 
- addElement("VIN Number", "vin","-",0x2, "") 
- addElement("Clear Trouble Codes", "clearDTC","-",0x0, "") 
- addElement("System Info >>>", "SysInfo_Menu",">>>",0x1, "") 
- addElement("Greetings", "greet","",0x1, "") 
- pageDone() 
- return oldvalue 
-end 
- 
- 
------------------ Setzen der Startbedingungen -------------- 
- 
-Start("","") 
-return 
-</code> 
- 
- 
-(Nebenbei gesagt: Hier siehst Du die start() Funktion in der Ausführung) 
- 
-Also was finden wir hier? Zuerst haben wir den Aufruf der Funktion  
-=== openPage("Title") === 
- 
-Diese Funktion teilt OOBD mit, das ein neues Menue erstellt werden soll. Der //Title// wird benutzt, welch Überraschung, für den Namen des Titels dieses neuen Menü's. 
- 
-Dann wird das Menü aufgebaut mit 
- 
-=== addElement(Description, function, initialValue, Flagset, id) === 
- 
-Dieser Funktionsaufruf fügt ein einzelnes Element zu der Liste hinzu. Diese Funktion hat die folgenden Parameter 
- 
-  * Description(String): Dies erscheint als Beschreibung an diesem Menüeintrag. 
-  * function: Jedes Menü ist einer Lua Funktion zugewiesen, wobei eine Funktion auf die diversen Menüeinträge aufgeteilt wird. Der Name dieser Funktion wird hier festgelegt ( Bitte achte auf die korrekte Groß- und Kleinschreibweise der Buchstaben ) 
-  * initialValue(String): Dies ist das, was angezeigt wird, wenn das Menü zum ersten Mal aufgeblendet wird. 
-  * Flagset(Integer): Dies ist ein Integer Wert, wobei jedes Bit ein Flag darstellt. Die Bedeutung dieser Flags wird erklärt in der [[de:dev:clientdesignguide|Benutzerschnittstellen Entwicklungsanleitung]] (Suche nach den System Flags) 
-  * id(String): Diese id wird benutzt, um einen individuellen Marker an einen Menüeintrag zu binden. Dies ist weit verbreitet, wenn eine einzige Lua Funktion viele Menüoptionen unterstützen sollte. Es wird im Detail beschrieben in den [[#die_menue_funktions_aufrufe|Menü Funktionsaufrufe]] weiter unten. 
- 
-=== pageDone() === 
- 
-Die pageDone() Funktion ist einfach, denn es teilt OOBD mit, daß das Menü nun komplett definiert und bereit zur Anzeige ist. 
- 
-Das entgültige Ergebnis sieht dann ähnlich wie dieses aus. (Du wirst feststellen, das sich das Skript leicht verändert hat, nachdem der Screenshot gemacht wurde.) 
- 
-{{:pics:oobdme-window.png|}} 
- 
- 
-==== Das Menü Funktionsaufruf ==== 
- 
-Wie oben gezeigt, haben wir nun die Menü Einträge aufgesetzt - aber nun wollen wir das etwas passiert, wenn der Benutzer einen Menue Eintrag auswählt, nicht wahr? 
- 
- 
-Lasst uns also annehmen, das ein Benutzer einen Eintrag auswählt. Was passiert jetzt? 
-  - OOBD schaut nach, welche Lua Funktion mit diesem Menüeintrag verbunden ist 
-  - dann wird diese Lua Funktion mit diesen beiden Parametern aufgerufen: 
-    * der aktuell dargestellte Wert dieses Eintrags 
-    * und die id, welche wir dem Menüeintrag gaben, während der Initialisierung 
-  - dann macht die aufgerufene Lua Funktion etwas z.B. eine Berechnung oder sie startet die Erstellung eines weiteren Untermenüs 
-  - wenn die Funktion abgearbeitet ist, gibt sie eine Zeichenfolge zurück 
-  - Diese Zeichenfolge wird dann als neuer Wert dieses Menüeintrags dargestellt 
- 
-Das ist erstmal alles, und es erfüllt den Zweck :-) 
- 
- 
-==== Die Kommunikation Kommandos ==== 
- 
-OOBD benutzt gegenwärtig eine serielle Kommunikation, um mit dem Diagnoseadapter zu kommunizieren. Um es Lua zu ermöglichen, Daten zum seriellen Port zu senden und von Ihm zu empfangen, existieren einige wenige erweiterte Lua Funktionen: 
- 
-=== serFlush() === 
- 
-Diese Funktion leert den Eingabespeicher (Von allem das, was möglicherweise in der Zwischenzeit empfangen wurde und nicht mehr benötigt wird)  
- 
-=== serWrite(String) === 
- 
-Diese Funktion sendet einen //String// zum Ausgabespeicher (Das ist normalerweise der serielle Port) 
- 
-=== serSleep(milliseconds) === 
- 
-Wartet //milliseconds// mit der Programmausführung 
- 
-=== serReadLn(msTimeout) === 
- 
-Liest von der Eingabe bis ein Zeilenvorschub (dez. 10 hex 0x0A) auftaucht und gibt Diese als Zeichenfolge zurück. 
- 
-=== serWait(OptionString, msTimeout) === 
- 
-Falls Du auf mehrere Zeichenfolgen wartest, von denen einige auftreten könnten, füllst Du den //Optionstring// mit string1|string2|..|stringN.  Wenn dann eine dieser Zeichenfolgen in der Eingabe auftaucht, wird der Zählindex zurückgegeben (erste Zeichenfolge=1). Wenn die Zeichenfolge nicht innerhalb einer vorgegebenen //msTimeout// Zeitspanne eintrifft, gibt die Funktion den Wert 0 zurück. 
- 
- 
-====  Sonstige Kommandos ==== 
- 
-=== serDisplayWrite(String) === 
- 
- 
-Schreibt eine //String// Zeichenfolge zu dem was schon in der Ausgabe steht. 
- 
-=== dbLookup(db-File , searchstring) === 
- 
-Sucht in der //db-file// Datenbank nach allen Einträgen mit dem Index //searchstring//. Die //db-file// Datenbank muss sich im gleichen Verzeichnis befinden, wie wie das Lua-Skript. Die //db-file// Datenbank selber wird erzeugt durch [[doc:oodbcreate|oodbCreate]]. 
- 
-dbLookup() gibt eine Lua Tabelle zurück 
-   
-  myTable = dbLookup("dtc.oodb", "0815") 
- 
-//myTable.len// gibt den Erfolgswert zurück: 
-  * if //myTable.len// < 0 dann ist ein Fehler aufgetaucht 
-  * if //myTable.len// = 0 dann ist die //Suchzeichenfolge// nicht gefunden worden. 
-  * if //myTable.len// > 0 dann zeigt //myTable.len// die Anzahl von Einträgen an, die gefunden wurden. 
- 
-Wenn etwas gefunden wurde, enthält //myTable// zwei Bereiche, //header// Kopfzeile und  //data// Daten. 
- 
-Der "header" Bereich wird benötigt, wenn Du nicht weißt in welcher Spalte Dein gewünschtes Ergebnis gespeichert ist. Du kannst dann die Spalte erkennen, in dem Du über den Kopfzeilennamen Dich annäherst: 
- 
-   col= myTable.header["DTC-Text"] 
-   print (col) 
-   2 
-    
- //myTable.header.size// gibt die Anzahl der Spalten insgesamt zurück **ohne** die erste Index Spalte, welche immer unterdrückt wird. 
- 
-Der //data// Daten Bereich enthält dann die gefundenen Daten, aufgebaut in einem zweidimensionalen Feld, sortiert nach Zeilen und Spalten. 
-    result=myTable.data[row][column] 
- 
-**Achtung**: Obwohl die Zeilen- und Spaltenindexe durch Zahlen dargestellt werden (1,2,3,4..), werden sie intern als Zeichenfolgen ("1","2","3","4"...) geführt. Um also das Ergebnis korrekt zu lesen, müssen die Zeilen- und Spaltenzähler zu Zeichenfolgen konvertiert werden, um das zweidimensionale Feld korrekt zu adressieren. 
- 
-    column=3 
-    row=2 
-    result=myTable.data[tostring(row)][tostring(column)]) 
-     
-  
-Nach all den theoretischen Informationen ein kleines Stück Beispielkode:    
- 
-<code lua> 
-myTable= dbLookupCall("dtc.oodb","005") 
- 
-print ("header") 
-for k,v in pairs (myTable.header) do 
-    print (k,"=",v) 
-end 
- 
-nrOfColumns = myTable.header.size 
-nrOfRows = myTable.len 
- 
-print ("Rows x Columns:" ,nrOfRows, nrOfColumns) 
- 
- 
-for row = 1 , nrOfRows , 1 do 
-  for column = 1 , nrOfColumns, 1 do  
-   
-   print (cy, cx, myTable.data[tostring(row)][tostring(column)]) 
-  end 
-end 
-</code> 
- 
-===== Ein paar Programmiertricks ===== 
- 
-Wenn Du Kenntnisse aus den althergebrachten Programmiersprachen mitbringst, wirst Du wahrscheinlich einfache und statische Variablentypen verwenden, wie Zahlen und Zeichenfolgen. Aber Lua offeriert hier mehr Komfort, besonders mit der Hilfestellung von geschachtelten (assoziativen) Feldern. Mit diesen können einige Dinge sehr viel einfacher realisiert werden. 
- 
-Wir wollen uns ein häufiges Szenario vorstellen: Du möchtest eine Anzahl von Menüeinträgen verwenden, welche alle das Gleiche tun, Einen Wert von einem Fahrzeug erhalten, nur mit verschiedenen Parametern. 
- 
-In der Vergangenheit musstest Du eine Funktion für jeden einzelnen Wert und einen lange Liste von Menüeinträgen schreiben. Aber in Lua kannst Du alle Parameter, deren Bedeutungen und auch eine Funktionsreferenz, um Sie zu bekommen, in einem einzigen Feld speichern wie 
- 
-<code> 
-local Menu2Data = { 
-id0x0815 = { byte = 1 , size =  1 , mult = 0.392156862745 , offset = 0, unit = " %", title="Part Number", call = "readAscPid"} , 
-id0x4711 = { byte = 1 , size =  1 , mult = 0.392156862745 , offset = 0, unit = " %", title="Software Level", call = "readAscPid"} , 
--- ... here are all the other parameter settings 
- 
-} 
-</code> 
- 
-Dann, während Deiner Menü Initialisierung, kannst Du Lua das Feld durcharbeiten und das Menü daraus aufbauen lassen. 
- 
-<code lua> 
- openPage("MyMenu") 
- for key,value in pairs(Menu2Data) do 
- res=_G[value.call]("-",key) --nice trick to call a function just by name :-) 
- addElement(value.title, value.call,res,0x1, key) 
- end 
- pageDone() 
- 
-</code> 
- 
-Und wieder einmal kann man sagen: Das war's. 
- 
-Wenn dann später eine Funktion aufgerufen wird, durch seinen Menü Eintrag, bekommt es einen Hash Key aus dem Menu2Data Feld als dessen id Parameter. Damit kann die Funktion, dann Ihre eigenen Parametersätze aus dem Menu2Data Feld auslesen um die korrekten Werte auszurechnen. 
- 
-Bitte beachte den Gebrauch des _G array Feldes in dem obigen Beispiel: Lua speichert alle Funktionen in sein globales _G array Feld, wo sie dann referenziert über Ihren Namen, als normale Funktion benutzt werden können. So macht der ''res=_G[value.call]("-",key)'' Aufruf das folgende: 
- 
-  - es findet eine Funktion mit Hilfe seines Namens 
-  - dann ruft es diese Funktion auf, wie auch der Nutzer es tun würde 
-  - es speichert den aktuellen Rückgabewert ( welcher die aktuellen Messdaten repräsentiert ) in res 
-  - res wird dann wie der initial dargestellte Wert, als das Menü konfiguriert wurde, benutzt  
- 
-Durch diese Vorgehensweise ist es möglich, ohne großen Aufwand ein Menü mit den reellen Daten während der Initialisierung zu füllen. So sind die aktuellen Daten von Anfang an direkt sichtbar. 
de/doc/lua_start.1383606476.txt.gz · Last modified: 2013/11/05 00:07 (external edit)