.. highlightlang:: us .. _formelinterpreter-3---erstellung-von-scriptformeln: Formelinterpreter 3 - Erstellung von Scriptformeln ================================================== Die meisten Formeln können über die Formeltabelle erzeugt werden. Bei Formeln, die mit der Tabelle bzw. über den Formel-Dialog nicht erzeugt werden können, weil sie zu kompliziert sind, können Skript-Formeln erzeugt werden. Bei Skriptformeln existieren die Einschränkungen der Tabellenformeln nicht. In Skript-Formeln können alle Sprachelemente von UniScript verwendet werden, z. B. if-else-Bedingungen oder for-Schleifen. .. _beispiel: Beispiel -------- Das folgende Beispiel zeigt eine einfache Skriptformel, die aus Drehzahl und Moment die Leistung berechnet: :: // Leistung in kW // M*2*PI*n // Drehzahl n in 1/min // Moment m in Nm def _fi_Power(bInfo) { ssChannels = "n_Motor, Md_Motor"; if (bInfo) { return ["Power"; .. "kW"; .. "Leistung"; .. ssChannels]; } n_Motor = ch("n_Motor"); Md_Motor = ch("Md_Motor"); Power = (n_Motor .* Md_Motor) ./ 9549.3; set_ch("Power", Power, ssChannels); } In Skript-Formel sollten die Operatoren ``.*``, ``./``, ``.^`` verwendet werden. Der Punkt vor dem Operator bedeutet, dass die Argumente elementweise verknüpft werden. ``Md_Motor`` und ``n_Motor`` sind Vektoren, die alle Datenpunkte des Kanals enthalten. Der Ergebnisvektor ``Power`` hat genau so viele Elemente wie ``Md_Motor`` und ``n_Motor``. Falls ``Md_Motor`` und ``n_Motor`` unterschiedlich viele Elemente enthalten würden, würde die Funktion mit einem Fehler beendet. Bei MDF-, Famos- oder ASAM-ODS-Daten können innerhalb einer Formel Kanäle mit unterschiedlicher Anzahl Datenpunkte verwendet werden. In diesem Fall werden die Kanäle durch lineare Interpolation auf die Auflösung des Kanals mit der größten zeitlichen Abtastrate interpoliert. Der Ergebniskanal wird dann zur Zeitgruppe des entsprechenden Kanals zugefügt. Die Funktion ch() muss in diesen Fällen einen zweiten Parameter enthalten. Der zweite Parameter enthält den Namen des Ergebniskanals. Beispiel: In der folgenden Formel wird der Kanal a, der mit 10 Hz abgetastet und der Kanal b, der mit 100 Hz abgetastet wurde, multipliziert. Das Ergebnis c ist ein Kanal mit 100Hz Abtastfrequenz. :: def _fi_c(bInfo) { ssChannels = "a, b"; if (bInfo) { return ["c"; .. ""; .. ""; .. ssChannels]; } _fich_a = ch("a", "c"); // a mit 10 Hz gemessen -> interpoliere auf 10Hz _fich_b = ch("b", "c"); // b mit 100 Hz gemessen -> interpoliere auf 10Hz _fich_c = _fich_a .* _fich_b; set_ch("c", _fich_c, ssChannels); } Beispiel: In der folgenden Formel werden im Ergebniskanal alle Werte auf 0 gesetzt, wenn die Drehzhahl, das Moment und die Geschwindigkeit sich in bestimmten Grenzen befinden. :: def _fi_wheel_0(bInfo) { ssChannels = "speed, torque, velocity, Wheel"; if (bInfo) { return ["Wheel_0"; .. "Nm"; .. "wheel torque"; .. ssChannels; .. ""; .. "%.1lf"; "0"]; } rvS = ch("speed", "Wheel_0", FALSE); rvT = ch("torque", "Wheel_0", FALSE); rvV = ch("velocity", "Wheel_0", FALSE); rvW = ch("Wheel", "Wheel_0", FALSE); rvW_0 = rvW; idx = find (rvS < 800 && rvT < 5 && (rvV < 0 || rvV > 160)); if (idx[1] != 0) { rvW_0[idx] = 0; } set_ch("Wheel_0", rvW_0, ssChannels, FALSE); } .. _elemente-einer-skriptformel: Elemente einer Skriptformel --------------------------- Damit die Formeln von UniPlot ausgewertet werden können, muss die Funktion die folgenden Vorschriften erfüllen: * Für jede Formel wird eine UniScript-Funktion erzeugt. * Jede Formel erzeugt einen neuen Kanal. * Der Name einer Funktion beginnt mit ``_fi_``. * Die Funktion hat einen Parameter ``bInfo``. * Wenn die Funktion mit dem Wert ``bInfo = TRUE`` aufgerufen wird, muss die Funktion einen String-Vektor mit mindestens 4 Elementen zurückgeben. Die Bedeutung der Elemente wird später beschrieben. * Die Funktion kann auf die Daten einer NC-Datei mit der Funktion :ref:`ch` (für "Channel") zugreifen. * Die berechneten Daten werden mit der Funktion :ref:`set_ch` in die aktuelle NC-Datei geschrieben. * Innerhalb der Funktion können alle UniScript-Funktionen aufgerufen werden. .. _abfrage-der-funktionsinformationen: Abfrage der Funktionsinformationen ---------------------------------- Wenn eine Formel-Funktion mit dem Wert ``bInfo == TRUE`` aufgerufen wird, muss die Funktion einen String-Vektor mit mindestens 4 Elementen liefern:: ssChannels = "n_Motor, Md_Motor"; if (bInfo) { return ["Power"; .. "kW"; .. "Leistung"; .. ssChannels]; } Element 1 Das erste Element ist der Name der Formel. Der Name muss den Namensregeln für Kanalnamen entsprechen. Element 2 Physikalische Einheit. Element 3 Kurze Beschreibung der Formel. Element 4 Liste der Kanalnamen, auf die in der Formel zugegriffen wird. Die Kanalnamen werden durch Kommas getrennt. Bevor die Funktion aufgerufen wird, prüft der Formelinterpreter, ob die Formel berechnet werden kann. Dazu schaut der Interpreter nach, ob die abhängigen Kanäle bereits berechnet wurden bzw. ob die Messkanäle in der Datei verfügbar sind. Element 5 Name einer Bedingung, z. B. Diesel. Wenn eine Bedingung angeben wird, muss eine Funktion vorhanden sein, die den Wert TRUE (1) oder FALSE (0) zurück liefert. Wenn die Formel berechnet werden soll, gibt die Funktion den Wert TRUE (1) zurück, andernfalls den Wert FALSE (0). Die Funktion beginnt mit dem Kürzel ``_fiis_`` gefolgt vom Namen der Bedingung in Kleinbuchstaben, z. B. ``_fiis_diesel``. Element 6 Format für die Zahlendarstellung der Daten, z. B. ``%g`` oder ``%.2lf``. Ein ausführliche Beschreibung der Formatdefinition finden Sie in der Dokumentation der Funktion :ref:`printf`. .. _definition-einer-bedingung: Definition einer Bedingung -------------------------- Das folgende Programm-Listing zeigt die UniScript-Funktion für die Bedingung "Diesel". Die Funktion geht davon aus, dass sich in der Datei ein Attribut (Stammdatum) mit dem Namen ``"BEnzin___DIesel"`` befindet, das entweder den Wert ``"BE"`` oder ``"DI"`` hat. :: def _fiis_diesel() { ncid = get_ncid(); if (ncid == -1) { error(); return FALSE; } if (nc_attinq_datatype(ncid, NC_GLOBAL, "BEnzin___DIesel") == NC_CHAR) { ssEngineType = nc_attget(ncid, NC_GLOBAL, "BEnzin___DIesel"); if (ssEngineType == "DI") { return TRUE; } } return FALSE; } Die Funktion :ref:`get_ncid` liefert die Zugriffsnummer der aktiven NC-Datei. Mit der Zugriffsnummer können Informationen aus der NC-Datei gelesen werden. Im Programmbeispiel wird geprüft, ob die Datei das globale Attribut ``"BEnzin___DIesel"`` enthält. Falls das Attribut den Wert "DI" besitzt, gibt die Funktion der Wert TRUE (1) zurück. In allen anderen Fällen den Wert FALSE (0). Das bedeutet, dass alle Formeln die als Bedingung den Wert "Diesel" im Info-Text enthalten, nur auf NC-Dateien angewendet werden, bei denen das Attribut "BEnzin___DIesel" den Wert "DI" besitzt. Beim Info-Text wird die Groß/Kleinschreibung nicht unterschieden. Sie können also auch DIESEL schreiben. Die Bedingungs-Funktionen beginnen mit der Zeichenfolge ``_fiis_`` gefolgt von einem Namen, der den Namensregeln von UniScript-Funktionen entsprechen muss. Für den Namen dürfen nur Kleinbuchstaben verwendet werden, z. B. ``_fiis_diesel``. Die Bedingungs-Funktionen können Sie z. B. in einer ic-Datei im Verzeichnis :file:`UniPlot\\samples` speichern. .. _funktionen-fur-formeln: Funktionen für Formeln ---------------------- Im Abschnitt :ref:`funktionen-in-formeltabellen`, wurden die vorhandenen Funktionen beschrieben. Dieser Abschnitt beschreibt wie Funktionen definiert werden. Wenn das Programm, das die Konvertierung von Tabellenformeln in UniScript-Funktion durchführt, Funktionsaufrufe findet, z. B. ``sqrt()`` ersetzt es den Namen durch ``_fif_sqrt()``. Es wird dadurch nicht die UniScript-Funktion :ref:`sqrt` aufgerufen, sondern die Funktion ``_fif_sqrt()``. Der Grund ist, dass die UniScript-Funktion :ref:`sqrt` "Missing Values" ignoriert (siehe :ref:`kanale-mit-missing-values`) und aus negativen Zahlen komplexe Zahlen berechnet. Die Funktion ``_fif_sqrt()`` ist folgendermaßen definiert:: def _fif_sqrt(rvData) { if (type(rvData) == "string") { if (rvData == "bInfo") { return ["1", "sqrt(c)"]; } } r = rvData; idx = find(rvData < 0 || rvData == MISSING_VALUE); if (idx[1] != 0) { r[idx] = 1; } r = sqrt(r); if (idx[1] != 0) { r[idx] = MISSING_VALUE; } return r; } Entsprechend können Sie eigene Funktionen für die Formeltabelle schreiben. Weitere Beispiele finden Sie in der Datei :file:`script\\fi-tools.ic`. .. _OnFormulaStartEval: .. _OnFormulaFinishEval: .. _die-funktion-onformulastarteval: OnFormulaStartEval ------------------ Die Funktion :ref:`OnFormulaStartEval` wird aufgerufen, wenn der Benutzer die Formelberechnung startet, z. B. über die :kbd:`F9`-Funkionstaste. Als Parameter wird der Funktion die Zugriffsnummer der aktiven NC-Datei übergeben. In der Funktion kann z. B. geprüft werden, ob die Formeln auf die aktive NC-Datei angewendet werden sollen. Falls die Funktion nicht existiert, werden die Formeln bei allen NC-Dateien berechnet. Die Funktion hat Lesezugriff auf die NC-Datei. Im folgenden Programm-Listing wird geprüft ob die NC-Datei ein globales Attribut "Creator" enthält und in diesem Attribut die Zeichenkette "Pasy" vorkommt. :: def OnFormulaStartEval(ncid) { if (nc_attinq_datatype(ncid, NC_GLOBAL, "Creator") == NC_CHAR) { ssCreator = nc_attget(ncid, NC_GLOBAL, "Creator"); if (strmatch("*Pasy*", ssCreator)) { return TRUE; } } return FALSE; } Die Funktion :ref:`OnFormulaStartEval` können Sie z. B. in einer ic-Datei im Verzeichnis :file:`UniPlot\\samples` speichern. In der Funktion kann auch das Formel-Verzeichnis mit Hilfe der Funktion :ref:`FE_SetFormulaDirectory` gesetzt werden. Falls eine Funktion :ref:`OnFormulaFinishEval` definiert ist, kann darin beispielsweise das in :ref:`OnFormulaStartEval` gesetzte Verzeichnis wieder auf das Originalverzeichnis zurückgesetzt werden. :: def OnFormulaStartEval(ncid) { svOldDir = FE_SetFormulaDirectory("C:/formula"); WriteProfileString("Formula", "olddir", strcat(svOldDir, ";")); return TRUE; } def OnFormulaFinishEval() { ssOldDir = GetProfileString("Formula", "olddir"); FE_SetFormulaDirectory(ssOldDir); } Formel registrieren ------------------- Wenn eine neu Formel ertellt und im Formelverzeichnis gespeichert wurde, muss die Library neu erzeugt werden. Beim Wechsel des Formelverzeichnisses oder bei der Erstellung einer neuen Formel mit Hilfe des Formeleditors wird die Lirary automatisch aktualisiert. Die Library wird ebenfalls neu erstellt, wenn das Dialogfeld :ref:`tools-formula-configuration` mit OK geschlossen wird. Ansonsten kann die Library mit Hilfe der Funktion ``_FE_UpdateFormulaList()`` neu erstellt werden. :sub:`id-71732`