Scripting-Grundlagen

<< Klicken Sie, um das Inhaltsverzeichnis anzuzeigen >>

Navigation:  »Kein übergeordnetes Thema«

Scripting-Grundlagen

EMIL lässt sich an mehreren Stellen per Script erweitern: bei der Berechnung eigener Formular-Scores (Formular Scripts), beim Import von Daten über die FELIX-Schnittstelle (FELIX Scripts) und bei der Erstellung von Berichten (Berichte Scripts). Dieses Kapitel beschreibt die Grundlagen, die in allen drei Bereichen gelten.

Hinweis: Scripting erfordert Programmiergrundkenntnisse. ITC bietet Trainings und die Erstellung von Scripts als kostenpflichtige Dienstleistung an (service@itc-ms.de); Scripting ist kein Bestandteil des kostenlosen ITC-Service.

Was ist ein Script?

Scripts sind kleine Programme, die EMIL ausführt. Ihr Handlungsspielraum ist so eingegrenzt, dass sie nur die vorgesehenen Funktionen ausführen und nicht in die Grundfunktionen von EMIL eingreifen können.

Ein Script kann z.B. Werte aus Formularfeldern lesen, daraus etwas berechnen und das Ergebnis einem Zielwert zuweisen. In Berichten geht ein Script eine Datenmenge (eine per SQL abgefragte Zeilenliste) durch und erzeugt daraus ein Dokument. An Schnittstellen sucht das Script in den empfangenen Daten nach Informationen, bereitet sie auf und überträgt sie in die Datenbank.

Grundaufbau eines Scripts

Scripts werden in Pascal Script geschrieben, das der Syntax der Programmiersprache Pascal folgt. Bei Bezeichnern (Variablennamen, Kommandos usw.) wird nicht zwischen Groß- und Kleinschreibung unterschieden, Hallo und hAlLo sind also identisch. Verwenden Sie trotzdem eine einheitliche Schreibweise. Umlaute und Leerzeichen dürfen nur innerhalb von Zeichenkettenkonstanten vorkommen. Sonderzeichen wie +, -, *, / und Klammern sind Rechenoperatoren und in Variablennamen nicht zulässig. Beschränken Sie Bezeichner auf Buchstaben und Zahlen; jede Variable muss mit einem Buchstaben beginnen.

Jedes Script besteht aus zwei Blöcken:

var

   ... Variablendefinitionen ...

begin

   ... Programmcode ...

end.

Kommandos und Definitionen werden immer mit einem Semikolon abgeschlossen.

Variablen

Alle Variablen, die das Script benötigt, müssen im var-Block definiert sein. Das verhindert, dass durch einen Schreibfehler mit einer anderen Variable weitergerechnet wird, und verhindert versehentliche Typvermischung. Gut lesbar sind CamelCasing (WertVorAenderung) oder Unterstriche (wert_vor_aenderung); beides ist gültig.

var

   Anzahl: Integer;

   Score: Double;

   Name: String;

Als Variablentypen stehen String, Integer, Double und Boolean zur Verfügung:

•Integer ist ein Ganzzahlentyp für Werte zwischen -2.147.483.648 und 2.147.483.647.

•Double ist eine Fließkommazahl zwischen etwa +/- 10308 mit einer Genauigkeit von 15-16 Dezimalstellen. Fließkommazahlen werden nicht unbedingt exakt gespeichert: 5.768 kann intern z.B. als 5.76800000000000001 vorliegen. Beachten Sie das bei Gleichheitsprüfungen und runden Sie Werte für die Ausgabe immer. Datumsangaben (mit oder ohne Zeit) werden in Scripts immer als Double verarbeitet: Der Wert ist die Anzahl der Tage seit dem 30.12.1899, der Teil hinter dem Komma ist der Bruchteil von 24 h, also die Uhrzeit.

•String (Zeichenkette) fasst zwischen 0 und rund zwei Milliarden Zeichen; die Grenze setzt allein der Hauptspeicher.

•Boolean ist ein Wahrheitswert und kann nur true oder false annehmen.

Zuweisung und Berechnung

Der eigentliche Code steht im begin/end-Block. Der Punkt hinter dem letzten end ist erforderlich, er markiert das Ende des Scripts. Blöcke können verschachtelt werden; innere Blöcke werden wie Kommandos mit Semikolon abgeschlossen, nur der äußerste mit einem Punkt:

begin

   ...

   begin

      ...

   end;

   ...

end.

Zuweisungen erfolgen mit :=, Vergleiche mit =:

var

   a, b, c: Integer;

begin

   a := 10;

   b := -7;

   c := a * 12 - b;

end.

Bei Fließkommazahlen (Double) ist immer der Dezimalpunkt zu verwenden, nicht das Komma:

var

   a, b, c: Double;

begin

   a := 10.6;

   b := -7.35;

   c := a * 12.88 - b;

end.

Zeichenketten (Strings) werden mit + verknüpft. Stringkonstanten stehen in einfachen Hochkommata (Shift-#), doppelte Anführungszeichen sind nicht zulässig:

var

   a, b, c: String;

begin

   a := 'Hallo';

   b := 'EMIL';

   c := a + ' ' + b;

end.

Je nach Kontext sind bereits Variablen vordefiniert, z.B. im Formular-Editor alle numerischen Eingaben und Ankreuzfelder. Auch FELIX und der Berichtegenerator haben eigene vordefinierte Variablen und Funktionen; sie sind in den jeweiligen Unterkapiteln dokumentiert.

Division: Fließkommawerte werden mit / geteilt, Ganzzahlen mit div; den Rest liefert mod.

Funktionen verwenden

Neben den Operatoren gibt es Funktionen. Die Standardfunktionen stehen weiter unten; dazu kommen kontextspezifische Funktionen, die nur in einem Bereich verfügbar und im jeweiligen Unterkapitel dokumentiert sind. Mit der Funktion Format wird z.B. ein Fließkommawert gerundet und in eine Zeichenkette umgewandelt:

var

   a: Double;

   b: String;

begin

   a := 3.5537 * 12.77 - 0.56473;

   b := Format(a, 3);

end.

Funktionen liefern einen Wert zurück (wie Format); Routinen, die nur eine Aktion ausführen, heißen in Pascal Prozeduren. Im Berichtegenerator gibt z.B. diese Prozedur eine Zeile aus:

   PrintContent('Gewicht (kg)', '99.66', true);

Die drei Parameter werden mit Komma getrennt als Argumente angegeben. In der Referenz steht dazu:

procedure PrintContent(const aLabel, aContent: String; AltColors: Boolean);

Anzugeben sind also ein Label und ein Inhalt als Strings (feste Strings oder Variablen) und als drittes Argument ein Wahrheitswert (true/false), der hier bestimmt, ob die Zeilen abwechselnd farbig hinterlegt werden.

Eigene Funktionen und Prozeduren erstellen

In manchen Scripts müssen Sie Funktionen definieren, die EMIL aufruft. Bei Berichten sind das die drei Phasen DefineParameters, CheckParameters und CreateContent, siehe Berichte Scripts.

Eine eigene Prozedur wird so erstellt und muss innerhalb des äußersten begin/end-Blocks definiert sein:

procedure defineParameters;

var

   x: Integer;

begin

   ...

end;

Funktionen und Prozeduren können eigene var-Blöcke haben. Diese lokalen Variablen gelten nur innerhalb der jeweiligen Funktion. Sie dürfen denselben Namen wie globale Variablen haben; vermeiden Sie das, weil es verwirrt.

Eigene Funktionen lohnen sich, wenn derselbe Code mehrfach gebraucht wird:

begin

   ...

   function doSomething(x: Integer): Integer;

   begin

     result := (x * 15 + 12 - 66) div 2;

   end;

   ...

   z := doSomething(564);

   v := doSomething(653);

   ...

end.

Jede Funktion hat die vordefinierte Variable result für den Rückgabewert. Sie hat den Datentyp, der im Funktionskopf hinter dem : steht (im Beispiel Integer).

Bedingungen

Ohne weitere Anweisungen wird ein Script zeilenweise von begin bis end ausgeführt. Mit if machen Sie Aktionen von Bedingungen abhängig:

   if (a > 6) or (b = 0) then

   begin

     c := 16;

     d := 'Hallo';

   end

   else

   begin

     c := -33;

     d := 'Otto';

   end;

Vergleichsoperatoren sind =, <>, >, <, <= und >=; verknüpft werden sie mit and und or. Der else-Zweig ist optional. if-Bedingungen lassen sich verketten:

if (a > 6) then

   text := 'größer sechs'

else if (a > 2) then

   text := 'größer zwei'

else

   text := 'kleiner oder gleich zwei';

Vor einem else darf kein Semikolon stehen. Bei einzeiligen Blöcken kann begin/end entfallen.

Bei Double-Werten ersetzen Sie die Gleichheitsabfrage wegen der begrenzten Genauigkeit durch einen Differenzvergleich:

if (abs(f - 1) < 1E-13) then ...

Die Differenz der beiden Zahlen wird ohne Vorzeichen (Abs) mit einem kleinen Epsilon verglichen, z.B. 1E-13 (=10-13). Ein direkter Vergleich von Fließkommazahlen mit = führt zu sporadischen Fehlern.

Schleifen

In Berichten und an Schnittstellen müssen Blöcke mehrfach ausgeführt werden, z.B. für jeden Datensatz einer Datenmenge oder jedes Segment einer Nachricht. Am häufigsten ist die while-Schleife:

var

   ds: TDataset;

begin

   ds := getDataset('... Abfrage', []);

   while not ds.eof do

   begin

     ...

     ds.next;

   end;

end.

Im Beispiel wird ein Dataset (eine per SQL erzeugte Datenmenge) geöffnet. Der begin/end-Block läuft, bis alle Datensätze abgearbeitet sind; ds.next wählt jeweils den nächsten Datensatz. ds.eof (End Of File) ist zunächst false und wird am Ende true, was die Schleife beendet.

Eine Zählschleife über einen Bereich von Ganzzahlen bildet for:

   for i := 0 to 9 do

   begin

     ...

   end;

Fehlerbehandlung

Tritt in einem Script ein Laufzeitfehler auf, wird er an EMIL weitergereicht. Bei der Scoreberechnung erscheint dann Error statt des Scores. Blöcke, die fehlschlagen können, sichern Sie mit try/except ab:

try

   ...

   // Block, der fehlschlagen kann

   ...

except

   ...

   // Block, der nur ausgeführt wird, wenn etwas fehlschlägt

   ...

end;

Standardfunktionen

Die folgenden Funktionen stehen in Formular-, Berichte- und FELIX-Scripts zur Verfügung. Ausnahmen sind in der Tabelle vermerkt.

Funktion

Rückgabe

Beschreibung / Beispiel

IntToStr(integer)

String

Wandelt einen Integer in einen String um: IntToStr(42) → '42'.

StrToInt(string)

Integer

Wandelt einen String in einen Integer um. Kann fehlschlagen: Bei ungültiger Eingabe wird eine Exception ausgelöst.

StrToIntDef(string, integer)

Integer

Wie StrToInt, liefert bei einem Fehler (leerer String, keine Zahl) aber den zweiten Parameter als Vorgabewert: StrToIntDef('abc', -1) → -1. Nützlich zum Auswerten von Combobox-Codes und Text-Eingaben.

StrToFloat(string)

Double

Pendant zu StrToInt für Fließkommazahlen. Erwartet den Dezimalpunkt. Kann fehlschlagen.

StrToFloatDef(string, double)

Double

Pendant zu StrToIntDef für Fließkommazahlen: StrToFloatDef(textfeldGewicht, -1.0). Nur in Formular-Scripts.

FloatToStr(double)

String

Pendant zu IntToStr für Fließkommazahlen.

Format(f: double; x: integer)

String

Formatiert f mit x Nachkommastellen als Zeichenkette: Format(3.5537 * 12.77 - 0.56473, 3) → '44.819'. Nur in Formular- und Berichte-Scripts.

roundTo(f: double; x: integer)

Double

Rundet f und liefert wieder eine Fließkommazahl; x ist negativ für Nachkommastellen (siehe unten). Ein leerer Wert (-NaN) bleibt leer. Nur in Formular- und Berichte-Scripts.

Pos(string, string)

Integer

Position des ersten Parameters im zweiten (1..n), 0 wenn nicht gefunden: Pos('lo', 'Hallo') → 4.

Copy(string, start, count)

String

Teil des Strings ab Zeichen start mit der Länge count: Copy('Hallo Welt', 7, 4) → 'Welt'.

Length(string)

Integer

Anzahl der Zeichen.

UpperCase(string)

String

Großbuchstaben. Umlaute werden nicht umgewandelt, dafür ist die Funktion sehr schnell.

LowerCase(string)

String

Pendant zu UpperCase mit Kleinbuchstaben.

AnsiUpperCase(string)

String

Großbuchstaben einschließlich Umlauten.

AnsiLowerCase(string)

String

Pendant zu AnsiUpperCase mit Kleinbuchstaben.

Trim(string)

String

Entfernt führende und nachfolgende Leerzeichen.

Sin(double)

Double

Sinus im Bogenmaß (2π = Vollkreis).

Cos(double)

Double

Cosinus im Bogenmaß (2π = Vollkreis).

Pi

Double

Konstante π.

Sqrt(double)

Double

Quadratwurzel.

Round(double)

Integer

Rundet kaufmännisch zur nächsten Ganzzahl.

Trunc(double)

Integer

Schneidet die Nachkommastellen ab: Trunc(-3.7) → -3.

Abs(double)

Double

Betrag (entfernt das Vorzeichen).

roundTo rundet auf die durch x angegebene Zehnerpotenz. Um Nachkommastellen zu runden, ist ein negatives x anzugeben: 1234.5678 wird bei x = 2 auf 1200 gerundet, bei x = -2 auf 1234.57. Format erwartet dagegen die Zahl der Nachkommastellen als positiven Wert.

gewichtKg := roundTo(gewicht_g / 1000, -2);

finalScore := Format(3.5537 * 12.77 - 0.56473, 3);  // liefert '44.819'

x := StrToIntDef(combobox1, -1);  // -1, wenn nichts ausgewählt ist