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

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

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 ch (für „Channel“) zugreifen.
  • Die berechneten Daten werden mit der Funktion set_ch in die aktuelle NC-Datei geschrieben.
  • Innerhalb der Funktion können alle UniScript-Funktionen aufgerufen werden.

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 printf.

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 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 UniPlot\Formula speichern.

Funktionen für Formeln

Im Abschnitt 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 sqrt aufgerufen, sondern die Funktion _fif_sqrt().

Der Grund ist, dass die UniScript-Funktion sqrt „Missing Values“ ignoriert (siehe Kanäle 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 script\fi-tools.ic.

OnFormulaStartEval

Die Funktion OnFormulaStartEval wird aufgerufen, wenn der Benutzer die Formelberechnung startet, z. B. über die 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 OnFormulaStartEval können Sie z. B. in einer ic-Datei im Verzeichnis UniPlot\Formula speichern.

In der Funktion kann auch das Formel-Verzeichnis mit Hilfe der Funktion FE_SetFormulaDirectory gesetzt werden.

Falls eine Funktion OnFormulaStartEval definiert ist, kann darin beispielsweise das in 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 Extras=>Formel-Konfiguration mit OK geschlossen wird. Ansonsten kann die Library mit Hilfe der Funktion _FE_UpdateFormulaList() neu erstellt werden.

id-71732