Hilfsfunktionen
Systemfunktionen
| Funktion | Beschreibung |
|---|---|
| time() | Liefert den Auswertezeitpunkt des Moduls in Sekunden UTC |
Eigenschaften eines Kanals oder Wertes
| Funktion | Beschreibung |
|---|---|
| deltaT(x) | Liefert den zeitlichen Abstand der letzten zwei Auswertezeitpunkte des Terms x in Sekunden |
| deltaY(x) | Liefert die Wertedifferenz der letzten zwei Samples des Terms x, ist positiv bei ansteigendem Signalverlauf |
| time(x) | Liefert den Zeitpunkt des letzten, geänderten Datensamples des Terms x |
| sgn(x) | Vorzeichen des Wertes x |
| sgn(x,eps) | Vorzeichen des Wertes x mit Toleranz (eps) |
| abs(x) | Absolutbetrag vom skalaren Wert x, Für die Länge eines Vektors siehe Length(V) |
| isConnected(x) | SmartCORE Verbindung abfragen |
| isEmpty(x) | Status der Datenquelle abfragen |
| isTimeout(x) | Datenlücke abfragen |
| isDefault(x) | Kanal auf Ersatzwert abfragen |
| isError(x) | Fehlerzustand eines Ergebnisses abfragen |
| isINF(x) | Gleitkommawert auf unendlich prüfen |
| isNAN(x) | Gleitkommawert auf NaN prüfen |
| isFinite(x) | Wert auf Verwendbarkeit prüfen |
| mkFinite(x,alt) | Nicht verwendbaren Wert ersetzen |
| typeOf(x) | Datentyp abfragen |
| value(x, ...) | Werte eines Kanals zeitlos abfragen |
| shiftT(x,dT) | Zeitliche Verschiebung von Kanaldaten |
SmartCORE Verbindung abfragen "isConnected" 1
Die Funktion liefert true, wenn der Kanal als Eingang mit einem smartCORE-Kanal erfolgreich verbunden wurde.
Wenn beim Start des smartCORE und des Math Modul kein smartCORE Kanal gefunden werden kann, wird dies nicht nur im Log-File ausgegeben, sondern auch über diesen Status signalisiert (false).
In diesem Fall wird der default Wert als Konstante in die Variable geschrieben, sodass Rechnungen ausgeführt werden können. Dieser ist zunächst false, kann aber über das #property-Macro mittels default=<variant> auf jeden beliebigen, konstanten Wert und Typ eingestellt werden. Siehe auch isDefault().
Die Abfrage liefert den Status für das laufende Batch-Intervall.
s1 = isConnected(x);
s2 = isConnected($'other.y');
Das Argument der Funktion muss unmittelbar eine Eingangsvariable sein.
Status der Datenquelle abfragen "isEmpty" 1
Die Funktion liefert true, wenn beim Start des smartCORE und des Math Modul noch keine Daten in den smartCORE Eingangskanal produziert wurden. Dies kann passieren, wenn andere Datenquellen erst nach einer Initialisierungsphase oder einem länger andauernden Aufbau einer Datenverbindung Daten produzieren können.
Tritt bei anhaltend leerem Kanal zusätzlich die Timeout-Bedingung bezogen auf den Startzeitpunkt des Math Moduls auf und ist ein expliziter Default-Wert über das #property-Macro mit der Option default=<val> gesetzt, wird dieser vorübergehend bis zum Eintreffen des ersten Datensamples als Ersatzwert verwendet. Siehe auch isDefault().
Die Abfrage liefert den Status für das laufende Batch-Intervall und ist unabhängig von den Dateninhalten und Zeitstempeln des Kanals.
s1 = isEmpty(x);
s2 = isEmpty($'other.y');
Das Argument der Funktion muss unmittelbar eine Eingangsvariable sein.
Datenlücke abfragen "isTimeout" 1
Die Funktion liefert true, wenn das jüngste Sample im smartCORE Eingangssignal älter als ist als ein einstellbares Timeout-Intervall und damit wahrscheinlich keine neuen Daten auf dem smartCORE Eingangssignal produziert wurden. Dies kann verschiedene Ursachen haben, wie z.B. das Ausbleiben einer CANbus-Botschaft, ein fehlendes GPS Signal oder eine fehlende Internet-Verbindung zur Abfrage von Steuerinformationen. Sobald neue Daten eintreffen, fällt der Status auf false zurück.
Für jeden Eingangskanal kann über das #property-Macro mit der Option timeout=<val> ein individueller Timeout-Wert zugewiesen werden. Dieser überschreibt für den Kanal die globale Einstellung mittels inputTimeoutS, die mit vorbelegt ist.
Tritt bei anhaltend leerem Kanal die Timeout-Bedingung bezogen auf den Startzeitpunkt des Math Moduls auf und ist ein expliziter Default-Wert über das #property-Macro mit der Option default=<val> gesetzt, wird dieser vorübergehend bis zum Eintreffen des ersten Datensamples als Ersatzwert verwendet. Siehe auch isDefault().
Sind bereits Datensamples im Kanal vorhanden, wird im timeout-Status der jüngste Wert der Variable in seiner Gültigkeit verlängert. Er läuft dann um das eingestelte Timeout-Intervall der aktuellen Ausführungszeit des Math Modul hinterher. Abhängige Berechnungen können dann bis zu diesem Zeitpunkt fortgeführt werden.
Dieser Zeitversatz ist nur dort zu sehen, wo Daten ohne Zeitstempel visualisiert oder verwendet werden, also z.B. in den MQTT Live-Daten, nicht aber in den OSF-Datein.
Die Abfrage liefert den Status für das laufende Batch-Intervall und ist unabhängig von den Dateninhalten und Zeitstempeln des Kanals.
s1 = isTimeout(x);
s2 = isTimeout($'other.y');
Das Argument der Funktion muss unmittelbar eine Eingangsvariable sein.
Kanal auf Ersatzwert abfragen "isDefault" 2
Die Funktion liefert true, wenn der Kanal dauerhaft oder temporär auf einen Default-Ersatzwert gesetzt ist.
Für jeden Eingangskanal kann über das #property-Macro mit der Option default=<val> ein individueller, konstanter Default-Wert zugewiesen werden. Falls dieser nicht gesetzt ist, wird false (<bool> => 0, 0.0, "false") verwendet.
Der Default-Wert wird als konstanter Ersatzwert verwendet, wenn der Kanal nicht mit einem smartCORE Kanal verbunden werden konnte.
Der Default-Wert wird als vorrübergehender Ersatzwert verwendet, wenn
- der Kanal verbunden wurde
isConnected() - und der Kanal noch leer ist
isEmpty() - und der Kanal in den Timeout gelaufen ist
isTimeout() - und
- explizit der Default-Wert über das
#property-Macro gesetzt wurde - oder der Kanal als
#timelessmarkiert wurde.
- explizit der Default-Wert über das
Die Abfrage liefert den Status für das laufende Batch-Intervall und ist unabhängig von den Dateninhalten und Zeitstempeln des Kanals.
s1 = isDefault(x);
s2 = isDefault($'other.y');
Das Argument der Funktion muss unmittelbar eine Eingangsvariable sein.
Fehlerzustand eines Ergebnisses abfragen "isError" 1
Die Funktion liefert true, wenn die Auswertung eines Terms einen Laufzeitfehler erzeugt hat. Die Fehlermeldung wird bei der Ausgabe der Variablen an den smartCORE auch im Log-File abgelegt oder kann als String im Math Modul weiter verwendet werden.
res = ...; // some expression
use = isError(res) ? 12.34 : res; // use alternate value on error
errCode = isError(res) ? int(res) : 0;
errMsg = isError(res) ? str(res) : 'ok';
Die Verwendung einer (lokalen) Variable, um das Ergebnis wiederzuverwenden, ist empfehlenswert.
Gleitkommawerte prüfen "isINF", "isNAN", "isFinite" 3
Gleitkommawerte (<dbl>, <cxFlt>) können durch Eingangsdaten oder Berechnungen die Sonderwerte unendlich (INF, positiv oder negativ) oder keine Zahl (NaN, Not a Number) annehmen. Diese Werte setzen sich durch nachfolgende Rechnungen fort und machen die Ergebnisse unbrauchbar.
Das Math Modul unterdrückt INF und NaN bereits beim Lesen aus einem smartCORE Kanal: solche Eingangssamples werden verworfen und gelangen nicht in den Formelsatz. Ebenso wird ein Ergebnis mit INF oder NaN niemals in einen smartCORE Kanal geschrieben, sondern verworfen. Beides wird im Log-File vermerkt.
INF und NaN können also praktisch nur im Kontext des Formelsatzes entstehen, z.B. durch einen Überlauf (x * 1.0e308) in einer Zwischenrechnung. Die Prüffunktionen dienen dazu, solche Zwischenergebnisse gezielt zu erkennen und zu ersetzen, bevor das Ergebnis verworfen wird.
| Funktion | liefert true, wenn ... |
|---|---|
| isINF(x) | x den Wert +INF oder -INF enthält |
| isNAN(x) | x den Wert NaN enthält |
| isFinite(x) | x weder INF noch NaN enthält, also verwendbar ist |
- Bei einer komplexen Zahl (
<cxFlt>) genügt es, wenn Real- oder Imaginärteil betroffen ist. - Bei Vektoren, Matrizen und Tensoren liefern
isINF()undisNAN()den Werttrue, sobald ein Element betroffen ist.isFinite()liefert nur danntrue, wenn alle Elemente endlich sind. - Alle übrigen Datentypen (
<bool>,<uint>,<int>,<str>) können wederINFnochNaNannehmen. Für sie liefernisINF()undisNAN()immerfalseundisFinite()immertrue. Ein String wird dabei nicht ausgewertet:isNAN('nan')istfalse. - Ein Laufzeitfehler im Argument wird unverändert weitergereicht, das Ergebnis ist dann selbst ein Fehler. Siehe
isError().
ok = isFinite(x);
bad = isNAN(x) || isINF(x); // identisch zu !isFinite(x)
y = isFinite(x) ? x : 0.0; // siehe auch mkFinite()
Nicht verwendbaren Wert ersetzen "mkFinite" 3
Die Funktion mkFinite(x, alt) liefert den Wert x, wenn dieser verwendbar ist (isFinite(x) ist true), sonst den Ersatzwert alt. Sie ist die kompakte Schreibweise für isFinite(x) ? x : alt.
y = mkFinite(x, 0.0);
v = mkFinite(V, [0, 0, 0]);
- Bei Vektoren, Matrizen und Tensoren wird bei einem einzigen nicht endlichen Element der gesamte Wert durch
altersetzt, nicht nur das betroffene Element.altsollte deshalb ein vollständiger, bekannter Ersatzwert passender Dimension sein. - Der Ersatzwert wird unverändert übernommen, auch in seinem Datentyp. Er selbst wird nicht geprüft.
- Ein Laufzeitfehler in
xwird nicht ersetzt, sondern weitergereicht. Zum Abfangen eines Fehlers dientisError().
Datentyp abfragen "typeOf" 1
Die Funktion typeOf liefert den Datentyp des übergebenen Wertes als ENUM Wert:
t = typeOf(x);
Der Werte haben folgende Entsprechung:
| Wert | Datentyp |
|---|---|
| 0 | void |
| 1 | <bool> |
| 2 | <uint> |
| 3 | <int> |
| 4 | <dbl> |
| 5 | <cxFlt> |
| 6 | <str> |
| > 0x80000000 | (Reserviert) |
| 0x8000B10B | BLOB - Binäres Objekt, z.B. einer transition() |
| 0x8000EBAD | ERROR - Laufzeitfehler eines Terms |
Werte eines Kanals zeitlos abfragen "value" 2
Diese Funktion ermöglicht, an einer bestimmten Stelle der Berechnung die Zeitabhängigkeit eines Berechnungspfades ähnlich dem #timeless aufzulösen.
Dazu wird der letzte vor dem Batch-Intervall (evaluationTimeMs) verfügbare Wert als Startwert für das Intervall verwendet, dann alle Samples mit Zeitstempeln im Batch-Intervall kopiert und zum Schluss der letzte Wert bis zum Ende des Intervalls ausgedehnt. Falls als Argument ein leerer Kanal verwendet wird, kann explizit ein Ersatzwert angegeben werden.
Die Abfrage liefert Werte für das laufende Batch-Intervall und ist unabhängig von den Dateninhalten und Zeitstempeln des Kanals.
v1 = value(x);
v2 = value(x, def); // use def, if isEmpty(x)
Typischerweise führt eine zirkuläre Referenz im Math Module zum Wechsel auf ein diskretes Abtastintervall (discreteSampleTimeMs) für den betroffenen Kanal, der dann auch mit einem explizitem Startwert (@0-Syntax) versehen sein muss:
zv@0 = 0; // startvalue for discrete calculation
zv = a * zv + (1 - a) * x; // example for circular use of 'zv'
Im Kontext von logischen Steuerungen, Statemachines, Grenzwertüberwachungen, etc. kann es sinnvoll sein, die Berechnungen zeitrichtig auszuführen und dennoch eine Rückführung eines Berechnungsergebnisses auf den Anfang der Berechnung zu nutzen. Die Funktion value() stellt genau die Stelle dar, an der die Rückführung lokal im Sinne von #timeless aufgebrochen wird.
Das Auflösen der Zeitabhängigkeiten führt an der entsprechenden Stelle zu einem Verhalten, das nicht mehr 100%ig vorhersehbar und definiert ist. Kurzzeitige Signaländerungen auf dem Eingangs-Term können übersehen werden oder Änderungen erst in einem späteren Batch-Intervall sichtbar werden. Die Auswirkungen der Funktion müssen vor einem produktiven Einsatz sorgfältig geprüft werden!
Zeitliche Verschiebung von Kanaldaten "shiftT" 2
Um einen Daten eines Kanals um eine bestimmtes ZeitinvervalldT zu verschieben, kann die Funktion shiftT() eingesetzt werden. Da eine Verschiebung und nachfolgend zeitrichtige Verrechnung mit anderen Daten immer zu einer Pufferung führt, sollte die Verschiebung auf wenige Sekunden begrenzt bleiben.
Zu allen Zeitstempeln der Daten des x-Terms wird das Zeitintervall dT in Sekunden addiert. Eine Verschiebung mit dT > 0.0 verschiebt die Daten in die Zukunft, dT < 0.0 in die Vergangenheit.
xs = shiftT(x, dT);
Mit dieser Funktion können bekannte Zeitverzögerungen durch die Datenübertragung vom tatsächlichen Erfassungssystem (AD-Wandler, MicroController) eines analogen Signals bis zum Empfang und der Zeitstempelung der Daten im smartCORE ausgeglichen werden. Dies kann zum Ausgleich von Phasenverschiebungen insbesondere bei sehr hochfrequent abgetasteten Signalen sinnvoll sein.
Kenngrößen mehrerer Werte
| Funktion | Beschreibung |
|---|---|
| abs(x1, ..., xN) | l2-Norm der Einzelwerte (x1, ..., xN), |
| min(x1, ..., xN) | Minimum der Einzelwerte (x1, ..., xN) |
| max(x1, ..., xN) | Maximum der Einzelwerte (x1, ..., xN) |
| mean(x1, ..., xN) | Arithmetischer Mittelwert der Einzelwerte (x1, ..., xN) |
| rms(x1, ..., xN) | Quadratwurzel des arithmetischen Mittelwerts der quadrierten Einzelwerte (x1, ..., xN) |
Begrenzen und Runden
| Funktion | Beschreibung |
|---|---|
| range(x, ...) | Begrenzt den Eingangswert auf einen bestimmten Wertebereich. Siehe nachfolgende Beschreibung. |
| ceil(x) | kleinster ganzzahliger Wert (datentyperhaltend) |
| ceil2i(x) | kleinster ganzzahliger Wert (Rückgabetyp int) |
| floor(x) | größter ganzzahliger Wert (datentyperhaltend) |
| floor2i(x) | größter ganzzahliger Wert (Rückgabetyp int) |
| trunc(x) | falls dann größter ganzzahliger Wert , sonst kleinster ganzzahliger Wert |
| round(x) | ganzzahliger Wert , so dass (datentyperhaltend) |
| round2i(x) | ganzzahliger Wert , so dass (Rückgabetyp int) |
| notZero(x, ...) | Folgt x, sofern , sonst wenn wird gesetzt, im Übrigen Siehe nachfolgende Beschreibung. |
Wertebereich "range"
Die Funktion schränkt den Wertebereich für ein gegebenes Eingangssignal x (Skalar oder Vektor) auf einen Bereich ein und bietet über die optionale Konfiguration vielfältige Varianten.
r1 = range(x);
r2 = range(x, [min, max]); // bessere Lesbarkeit
r3 = range(x, min, max); // nicht mehr verwenden
// Optionale Konfiguration für alle Varianten
rx = range(..., { min: off|<val>
, max: off|<val>
, lower: off|<val>|erase
, upper: off|<val>|erase
, mode: <enum>
, defXY: [<dbl>, <dbl>]
, limVar: <str>
});
| Eigenschaft | Wert | Beschreibung |
|---|---|---|
| min | off/<val> | Deaktivierung oder feste Konfiguration einer unteren (skalaren) Grenze. Wird der Funktionsparameter min genutzt, ist diese Eigenschaft ohne Funktion. |
| max | off/<val> | Deaktivierung oder feste Konfiguration einer oberen (skalaren) Grenze. Wird der Funktionsparameter max genutzt, ist diese Eigenschaft ohne Funktion. |
| lower | off/<val>/erase | Falls aktiviert, wird der Ersatzwert beim Überschreiten der unteren Grenze (x < min) ausgegeben oder das Sample entfernt |
| upper | off/<val>/erase | Falls aktiviert, wird der Ersatzwert beim Überschreiten der oberen Grenze (x > max) ausgegeben oder das Sample entfernt |
| mode | <enum> | Falls x ein Vektor ist, werden in Abhängigkeit von mode - box: alle Koordinaten einzeln… - length: die Länge des Vektors ( >= 0.0 )... - azimuth: der Winkel des Vektors in der XY-Ebene... - elevation: der Winkel des Vektors aus der XY-Ebene zur Z-Achse… ...auf den eingestellten Bereich limitiert |
| defXY | [<dbl>, <dbl>] | Im elevation-Mode ist defXY ist die Standardrichtung, wenn der Vektor aus der senkrechten zurückgeschwenkt werden muss (def.: [1, 0, 0]). |
| limVar | <str> | limVar definiert optional den Namen einer lokalen <bool>-Variable, um die aktive Limitierung zu signalisieren |
Nicht-Null "notZero"
Die Funktion sorgt dafür, dass ein kritischer Wert (def.: 0.0) niemals ausgegeben wird. Statt dessen wird der Wertebereich mittels eine Größe (def.: 1e-6) um die kritische Größe ausgespart. Die Anwendung der Funktion ist z.B. für Divisionen optimiert, bei denen eine Division durch Null möglich wäre und ausgeschlossen werden muss.
xnz1 = notZero(x); // eps := 1e-6, x_crit := 0.0
xnz2 = notZero(x, eps); // x_crit := 0.0
xnz3 = notZero(x, eps, x_crit);
Ein Beispiel ist die sichere Umrechnung von Krafstoff-Volumenstrom in und Geschwindigkeit in in einen Verbrauch in :
consumption = volFlow / notZero(speed, 0.1) * 100;
Schaltschwelle mit Hysterese "threshold"
Diese Funktion erzeugt ein Schaltsignal aus dem Vergleich eines Signals gegen einen festen Schwellenwert threshold. Mittels einer Hysterese hysteresis und Verzögerungszeiten delayOn, delayOff können Störungen auf dem Signalverlauf unterdrückt werden.
Die Funktion kann in verschiedenen Anwendungsformen verwendet werden:
trig1 = threshold(x);
trig2 = threshold(x, t);
trig3 = threshold(x, t, h);
// Optionale Konfiguration für alle Varianten
trigX = threshold(..., { threshold: <dbl>
, hysteresis: <dbl>
, delayOn: off|<time>
, delayOff: off|<time>
, logic: <enum>
, startup: inf|<bool>
});
| Eigenschaft | Wert | Bedeutung |
|---|---|---|
| threshold | <dbl> | Auslöseschwelle, def.: 0.55 Wird mit Argument t überschrieben |
| hysteresis | <dbl> | Breite der Hysteresezone, wird auf den threshold für den zweiten Grenzwert addiert, def.: -0.10 Wird mit Argument h überschrieben |
| delayOn | off/<time> | optionale Filterzeit in Sekunden für den Einschaltvorgang off3 oder 0: keine Filterzeit (def.) |
| delayOff | off/<time> | optionale Filterzeit in Sekunden für den Ausschaltvorgang off3 oder 0: keine Filterzeit (def.) |
| logic | <enum> | - auto: Funktion wird durch das Vorzeichen der Hysterese bestimmt - above: Auslösung oberhalb der Hysterese-Zone - below: Auslösung unterhalb der Hysterese-Zone |
| startup | inf|<bool> | Startverhalten: inf: Die Lage des ersten Samples bestimmt bezüglich des Thresholds den Ausgangswert (def.). <bool>: Es wird angenommen, dass dieser Ausgangswert vor dem ersten Sample ausgegeben wurde. |
- Die Funktion liefert true für den Zeitpunkt, wo das Signal x die definierte Auslöseschwelle überschreitet und false für den Zeitpunkt, wo das Signal x in die Rückstellung fällt.
Werden die Verzögerungszeiten eingestellt, muss das Signal x kontinuierlich für das angegebene Intervall in der Auslösung oder Rückstellung verbleiben, bevor die Umschaltung realisiert wird. Die Umschaltung ist dadurch gegenüber dem Überschreiten des jeweiligen Grenzwertes verzögert.
Ist das Eingangssignal linear interpolierbar markiert, wie z.B. der Ausgang von integrate(), verläuft es zwischen zwei Datenpunkten als Rampe. Die Umschaltung erfolgt dann zum Zeitpunkt, an dem die Rampe den Grenzwert erreicht, und die Verzögerungszeiten laufen ab diesem Zeitpunkt; verlässt die Rampe die Auslösung oder Rückstellung vor Ablauf der Verzögerung wieder, unterbleibt die Umschaltung. Der Ausgang selbst bleibt ein Schaltsignal. Die Werte NaN und INF werden durch den zuletzt eingegangenen endlichen Wert ersetzt; vor dem ersten endlichen Wert gibt die Funktion nichts aus. Ein nicht endliches t oder h lässt den zuletzt gültigen Grenzwert bzw. die Hysterese bestehen. Akzeptiert werden für x, t und h nur numerische Skalare; bei einem Vektor, einer Matrix, einem String oder einem komplexen Wert liefert threshold() die Fehlermeldung Input Value is not a numerical scalar.
In der Betriebsart auto (default) erfolgt die Auslösung in Abhängigkeit vom Vorzeichen der Hysterese:
| Hysterese | Auslösung (true) | Rückstellung (false) |
|---|---|---|
In den Betriebsarten above und below definieren threshold und hysteresis zwei Grenzen einer Hysterese-Zone:
| Betriebsart | Auslösung (true) | Rückstellung (false) |
|---|---|---|
| above | ||
| below |
Steilheitsbegrenzung "rampLimit" 4
Die Funktion sorgt dafür, dass Änderungen auf dem Eingangssignal x mit einer begrenzten Rampensteilheit weitergegeben werden. Optional können ein oder zwei zusätzliche Parameter verwendet werden, um entweder einen richtungsunabhängigen maximalen Gradienten zu definieren oder unterschiedliche für steigende und fallende Funktionsverläufe.
y1 = rampLimit(x, maxGrad);
y2 = rampLimit(x, maxPosGrad, maxNegGrad);
| Eigenschaft | Wert | Bedeutung |
|---|---|---|
| x | <dbl> | Eingangsssignal |
| maxGrad | <dbl> | Gemeinsamer maximaler Gradient für steigende und fallende Signalverläufe |
| maxPosGrad | <dbl> | Maximaler Gradient für steigende Signalverläufe |
| maxNegGrad | <dbl> | Maximaler Gradient für fallende Signalverläufe |
Die Einheit der maximalen Gradienten ist [Einheit von x] / s. Die Angabe ist vorzeichenlos, d.h. . Ein Gradient von 0.0 schaltet die Funktion in der entsprechenden Richtung auf Bypass: das Eingangssignal wird direkt ausgegeben.
Die Werte NaN und INF werden durch den zuletzt eingegangenen endlichen Wert ersetzt; vor dem ersten endlichen Wert gibt die Funktion nichts aus, der erste endliche Wert startet den Ausgang. Ein nicht endlicher Gradient lässt den zuletzt gültigen bestehen. Akzeptiert werden nur numerische Skalare; bei einem Vektor, einer Matrix, einem String oder einem komplexen Wert liefert rampLimit() die Fehlermeldung Input Value is not a numerical scalar.