Zum Hauptinhalt springen

Tabellen und Datenbanken

Datenfelder, Tabellen und Datenbanken

Für die folgenden Funktionen werden benötigte Tabellen als Ressource an die Funktion übergeben. Die Tabelle wird intern gelesen und aufbereitet.

Als Datenfeld wird je nach Eingangswert zwischen einzelnen Datenelementen interpoliert.

Als Tabelle mit Feldnamen können in bestimmten Schlüsselfeldern nach einem passenden Eintrag gesucht und verschiedene Felder dieses Eintrags ausgelesen werden.

Die Implementierung einer sqlite-Schnittstelle für den Zugriff auf eine lokale Datenbank-Instanz mit definierbaren Suchabfragen ist in Planung.

Kennlinie, Kennfeld "lookup"

Mit der Funktion lookup() können aus Kennlinien oder Kennfeldern Daten zu ein bis drei Eingangsgrößen linear interpoliert werden.

L1 = lookup(x, {...});
L2 = lookup(x, y, {...});
L3 = lookup(x, y, z, {...});
// Das Konfigurationsobjekt ist verpflichtend und muss
// auf eine Ressource verweisen:
Lx = lookup(..., {$ref:'myLookupData'});

Das Konfigurationsobjekt folgt einer eigenen Syntax und muss deshalb mittels {$ref:'myLookupData'} aus dem Ressourcebereich des Mathemoduls bezogen werden. Zwei Formate werden unterstützt: das klassische Text-Format und eine Beschreibung im vereinfachten JSON Format1

Beschreibung der Eingänge / Achsen

Jedem Eingangssignal wird eine Achse zugewiesen. Diese ist durch ihre Einheit <unit>, einen Startwert <from>, das Increment <step> und die Anzahl der Stützstellen <count> definiert. Die Achsen müssen nicht zwingen in numerisch aufsteigender Folge sortiert sein.

Konfiguration im Textformat (klassisch)

Für jede Achse ist folgende Beschreibung am Anfang des Textblocks hinzuzufügen, also mindestens eine, maximal drei:

IN <unit> <from> <step> <count>
Konfiguration im JSON-Format:

Im JSON Format ist zusätzlich die Angabe eines beschreibenden, informellen Namens <name> und alternativ zu einer der vorher genannten Werte-Parameter der Endwert <to> möglich. Es müssen also genau drei der vier Eigenschaften <from>, <step>, <count> oder <to> definiert werden, die jeweils vierte wird berechnet.

{
axes: [
{ name: <str>, // optional description
unit: <str>, // recommended
// choose 3 as mandatory of the following 4:
from: <dbl>, // left-side value of input axis
step: <dbl>, // increment between interpolation points
count: <uint>, // number of interpolation points
to: <dbl> // right-side value of input axis
}, ...
], ...
}

Beschreibung der Ausgänge

Das Ausgangssignal wird durch eine Einheit <unit> beschrieben.

Konfiguration im Textformat (klassisch)

Die folgende Definition muss einmal nach der Definition der Eingänge aufgeführt werden:

OUT <unit>
Konfiguration im JSON-Format:

Im JSON Format ist zusätzlich die Angabe eines beschreibenden, informellen Namens <name> möglich. Statt des JSON Objekts für jeden Ausgang (empfohlen) kann auch nur die physikalische Einheit als String angegeben werden.

{
outputs: [
{ name: <str>, // optional description
unit: <str> // recommended
}, ... // prepared to support vector output in future
<str>, ... // alternative to define output by unit
], ...

Datenfeld

Das Datenfeld besteht aus <dbl> Werten. Die Reihenfolge der Stützstellen ergibt sich aus folgendem Pseuso-Code:

foreach(z : axis3)
foreach(y : axis2)
foreach(x : axis1)
Value[x,y,z];
Konfiguration im Textformat (klassisch)

Die Datenwerte werden einfach durch Leerzeichen, Tabs oder Zeilenvorschübe beliebig voneinander getrennt und der Reihe nach in die Stützstellen der Kennlinie oder des Kennfeldes eingelesen.

0 42.942 87.924 134.946
184.008 235.11 288.252
... continued up to 161 data points
Konfiguration im JSON-Format:

Im JSON Format werden alle Datenwerte in einem Array aus Fließkommazahlen angegeben.

{
data: [
<dbl>, ...
], ...
}

Zusammenfassung

Konfiguration im Textformat (klassisch)

Kennlinie:

IN    kPa    0.0    0.167751    161
OUT l

0
42.942
87.924
134.946
184.008
235.11
288.252
343.434
400.656
459.918
521.22
... continued up to 161 data points

Kennfeld:

IN    g/h    0    10000    22
IN 1/min 900 100 10
OUT kW
0.0 40.8 81.6 132.9 185.2 200.0 200.0 200.0 200.0 200.0 200.0 200.0 200.0 200.0 200.0 200.0 200.0 200.0 200.0 200.0 200.0 200.0
0.0 40.0 80.0 131.3 184.3 230.0 230.0 230.0 230.0 230.0 230.0 230.0 230.0 230.0 230.0 230.0 230.0 230.0 230.0 230.0 230.0 230.0
0.0 38.5 76.9 125.8 182.4 233.5 286.9 310.0 310.0 310.0 310.0 310.0 310.0 310.0 310.0 310.0 310.0 310.0 310.0 310.0 310.0 310.0
... 7 more rows with 22 data points each
Konfiguration im vereinfachten JSON-Format

Das Konfigurationsobjekt kann auch im vereinfachten JSON Format1 beschrieben werden.

{
    axes: [
  { name: <str>,        // optional description
        unit: <str>,        // recommended
        from: <dbl>,        // left-side value of input axis
        step: <dbl>,        // increment between interpolation points
        count: <uint>,       // number of interpolation points
        to: <dbl>         // right-side value of input axis
}, ...
    ],
    outputs: [
{ name: <str>,        // optional description
        unit: <str>         // recommended
}, ...                 // prepared to support vector output in future
     <str>, ...             // alternative to define output by unit
    ],
    data: [
<dbl>, ...
    ]
}

Bereitstellung als Ressource

Es gibt vielfältige Möglichkeiten, diese Kennlinien oder Kennfeldbeschreibung in den Ressourcen des Mathemoduls aufzunehmen.

Für kleine Kennfelder oder Kennlinien eignet sich die Formatierung als Text-Ressource:

    "resources": {
"myLookupData": [
"IN kPa 0.0 0.167751 161"
, "OUT l"
, "0 42.942 87.924 134.946 184.008 235.11 288.252 343.434"
, "400.656 459.918 521.22 584.562 649.944 717.366 785.90745"
, "854.45145 ... and more"
], ...

Alternativ das selbe Kennfeld im vereinfachten JSON Format:

    "resources": {
"myLookupData": {
axes: [{ name: "HydroStatPressure", unit: "kPa"
, from: 0.0, step: 0.167751, count: 161 }],
outputs: [{ name: "Volume", unit: "l" }],
data: [
0, 42.942, 87.924, 134.946, 184.008, 235.11, 288.252, 343.434
, 400.656, 459.918, 521.22, 584.562, 649.944, 717.366, 785.90745
, 854.45145, // ... and more
]
}, ...

Für größere Kennfelder oder solche, die in der JSON Konfiguration des smartCORE nicht auftauchen oder lesbar sein sollen, wäre eine verarbeitete Zeichenkette sinnvoll.

Zunächst mit einer Kennfelddatei myLookupData.tab als Datenquelle, die der oben genannten Syntax (Text/Json) folgt:

    "resources": {
"myLookupData": {
"decoder": ["file"],
"value": "myLookupData.tab"
}, ...

oder, falls das Kennfeld in komprimierter Form vorliegt:

    "resources": {
"myLookupData": {
"decoder": ["file", "unzip"],
"value": "myLookupData.gz"
}, ...

Alternativ können die Daten auch als base64 - String in der Ressource aufgenommen werden:

    "resources": {
"myLookupData": {
"decoder": ["base64"],
"value": "Q09MCWcvaAkwCTEwMDAwCTIyDQpST1cJMS9taW4JOTAwCTEw..."
}, ...

... oder die komprimierte Datei als base64 - String:

    "resources": {
"myLookupData": {
"decoder": ["base64", "unzip"],
"value": "UEsDBBQAAgAIACt3cUoHOG6qmgEAAD4FAAAcAAAAUF9lZmZf..."
}

Suchabfrage "selectRow"

Die Funktion selectRow() such in einer einfachen Tabelle sequentiell durch alle Zeilen nach einer Übereinstimmung mit den angegebenen Schlüsseln und gibt den gefundenen Zeilenindex zurück. Wir keine Übereinstimmung gefunden, wird -1 zurückgegeben. Eine Suche wird nur dann ausgeführt, wenn sich mindestens einer der angefragten Schlüsselwerte ändert.

Dieser Zeilenindex kann in getField() verwendet werden, um beliebige Werte aus den Datenfeldern der Tabelle zu lesen.

m1  = selectRow(key1, ..., { table: <str>      // mandatory
, colSep: <regex>
, anyKey: <var>
, key: <str>        // mandatory (*)
, keys: [<str>]     // mandatory (*)
});
// (*) exactly one of key or keys must be defined
EigenschaftWertBeschreibung
table<str>Verpflichtend: Name einer Text-Ressource, die den Inhalt der Tabelle definiert. Die Datenelemente werden als Varianten interpretiert und liefern somit den bestmöglichen Datentyp. Fließkommazahlen sind mit einem Punkt als Dezimaltrennzeichen zu schreiben.
colSep<regex>Regular Expression, der die Spaltentrennzeichen definiert. Default: "[,;|\t]", universell verwendbar für jeweils ein einzelnes der in [] angegeben Zeichen.
anyKey<var>Ein Datenelement dieses Werts liefert für beliebige Schlüsselwerte ein positives Ergebnis, default: "*"
key<str>Verpflichtend (*, **): Ein Datenfeld der Tabelle, in dem der erste und einzige Schlüsselwert key1 gesucht wird.
keys[<str>]Verpflichtend (*, **): Ein Vektor mit mehreren Datenfeldern, die in der angegebenen Reihenfolge auf die Schlüsselwerte key1, key2, key3, ... anzuwenden sind.

(*) Es muss genau eine der Eigenschaften key oder keys verwendet werden, um die Schlüsselspalte(n) auszuwählen.

(**) Als Default wird immer eine exakte Übereinstimmung des Schlüsselwertes mit den Einträgen in der Tabellenspalte gesucht. Dem Bezeichner des Feldes können optional folgende Vergleichsoperatoren vorangestellt werden, um die Abfrage zu modifizieren, Leerzeichen dürfen zwischen Operator und Bezeichner eingefügt sein:

OperatorBedeutungBeispielAusführung wie...
KEY sei ein Feldbezeichner in keys:[..]keyN sei der als Parameter übergebene Schlüsselwert
==Exakte Übereinstimmung (Default)'KEY'
'==KEY'
keyN == Table[KEY]
*=für <str>:
enthält
*=KEYkeyN containes Table[KEY]
&=für <uint>|<int>:
alle Bits der Maske gesetzt
'&=KEY'bAnd(keyN, Table[KEY]) == Table[KEY]
&>für <uint>|<int>:
mindestens ein Bit der Maske gesetzt
'&>KEY'bAnd(keyN, Table[KEY]) > 0
&0für <uint>|<int>:
kein Bit der Maske gesetzt
'&0 KEY'bAnd(keyN, Table[KEY]) == 0
<=Kleiner oder gleich'<=KEY'keyN <= Table[KEY]
<Kleiner als'<KEY'keyN < Table[KEY]
>=Größer oder gleich'>=KEY'keyN >= Table[KEY]
>Größer als'>KEY'keyN < Table[KEY]

Für den Datentyp <str> beziehen sich die Vergleiche auf die alphabetische Reihenfolge.

Formatierung der Tabelle

  • Die Tabelle wird aus einer Text-Ressource geladen (ASCII, UTF-8)

  • Spaltentrennzeichen sind über die Eigenschaft colSep definiert.

  • Die selbe Tabelle kann in mehreren selectRow() Funktionen verwendet werden. Die colSep Angabe muss bei allen identisch sein, da nicht festgelegt ist, welche der Funktionen die Tabelle zuerst (und damit auch für alle anderen Instanzen) interpretiert.

  • Die erste Zeile enthält die Feldnamen, mit denen dann auch die Schlüssel-Spalten und Ergebnis-Felder definiert werden.

  • Leerzeilen werden ignoriert.

  • Zeilen, die mit 3 gleichen Zeichen aus der Gruppe _ - = ~ * beginnen, werden als Trennlinie interpretiert und ignoriert.

  • Jedes Datenfeld wird individuell als Variant gelesen und verarbeitet. Feste Datentypen für eine Spalte gibt es demnach nicht.

  • Die Suche erfolgt immer sequentiell durch alle Zeilen.

Tabelleneintrag auslesen "getField"

Die Funktion getField() liest aus einer mit selectRow() interpretierten Tabelle table zu dem gefundenen Zeilenindex rowIdx das zugehörende Datenelement aus dem Datenfeld field. Der Datentyp ist durch das Datenelement als Variant bestimmt.

Ist rowIdx < 0 oder außerhalb des gültigen Bereichs, wird der mit notFound definierte Variant zurück gegeben.

v1 = getField(rowIdx, { table: <str>      // mandatory
                , field: <str>     // mandatory
, notFound: <var>
});
EigenschaftWertBeschreibung
table<str>Verpflichtend: Name einer Text-Ressource, die den Inhalt der Tabelle definiert. Diese muss in einer selectRow() Funktion interpretiert und geladen worden sein.
field<str>Verpflichtend: Name eines Datenfeldes, um daraus den Ergebniswert auszulesen.
notFound<var>Dieser Variant wird als Ergebnis geliefert, falls rowIdx kein gültiges Suchergebnis bezeichnet. Default: false.

Wiedergabe aufgezeichneter Daten "playback" 2

Under Construction Diese Funktion ist in der Umsetzung und noch nicht für den produktiven Betrieb vorgesehen.

Die Funktion playback() öffnet eine "Tape"-Datei und extrahiert aus dieser Zeitverläufe ausgewählter Datenkanäle "Tracks".

warnung

Je nach Struktur und Länge der verwendeten Datenquellen kann es noch zu Speicherverletzung und Abstürzen des smartCORE kommen.

Die Tracks können mit einem zusätzlichen Prefix als Variablen im Math Modul veröffentlicht und dann als output-Variable auch an den smartCORE übergeben werden.

Vergleichbar einer klassischen Tonbandmaschine können Marker für die Wiedergabe eines bestimmten Zeitabschnitts gesetzt werden und Steuerfunktionen (Stop, Play, Pause, Rewind) auch über eine erweiterte Logik ausgelöst werden.

Studer A800

Derzeit wird das unkomprimierte OSF-Dateiformat unterstützt. Andere Formate wie z.B. WAV, CSV, ... könnten in zukünftigen Versionen folgen.

tipp

Verläufe, die von einem Tape ins System eingespielt werden, können als Referenz für Messsignale verwendet werden, um deren dynamischen Veraluf nach einem Startereignis zu verfolgen und zu überwachen.

tipp

Um neue Funktionen im Math Moduls zu testen, können auf einem stationären Laborsystem Messdaten aus dem produktiven Einsatz ein Echtzeit wieder eingespielt werden. optiCONTROL bietet für die Datenvorbereitung verschiedene Möglichkeiten zum Zusammenführen, Schneiden oder einer Kanalauswahl.

tipp

Um die OSF-Dateien auf dem /sde-Laufwerk vor der automatischen Komprimierung zu schützen, kann einfach eine andere Dateiendung, z.B. *.osf_pb oder *.osf_tape verwendet werden.

t1 = playback(ctrl, {...});
t2 = playback(ctrl, tInfo, {...});
t3 = playback(ctrl, tInfo, source, {...});
// Configuration is mandatory for paths and keys
tx = playback(..., { directory: <str>     // mandatory!
, source: <str>        // mandatory! (or by parameter)
                   , varPrefix: <str>
                 , keys: [<str>] // mandatory!
    , tBegin: <date>
                 , tRewind: <date>
             , tEnd: <date>
, result: <enum>
, loop: <bool>
, tZoom: <dbl>
, endVar: <str>
});

Der Parameter tInfo kann einen skalaren Wert oder einen Vektor mit bis zu 3 Komponenten enthalten. Die Beschreibung ist ebenfalls in nachfolgender Tabelle enthalten.

EigenschaftWertBeschreibung
directory<str>Pfad zum Verzeichnis mit Tapes
source<str>Initiales Tape, kann durch Parameter überschrieben werden. Jede Änderung lädt die per keys ausgewählten Kanäle vom bezeichneten Tape
varPrefix<str>Das Prefix wird den ausgewählten Kanälen für die Veröffentlichung als Variable vorangestellt, z.B. "Tape."
keys[<str>]Liste mit Kanalnamen aus der Tape-Datei, die zur Wiedergabe geladen werden.
tBegin<date>Startzeitpunkt auf dem ausgewählten Tape
tRewind<date>Zielpunkt zum Rück- oder Vorspulen
tEnd<date>Endzeitpunkt für die Wiedergabe oder automatischer Rücksprung
result<enum>Ausgabe der Tape-Position als
abs: absolter Zeitstempel (default)
relOrigin: relativer Zeitstempel zum Tape-Anfang
relBegin: relativer Zeitstempel zur tBegin-Marke
relInterval: relative Position [0..1][0..1] im Intervall [tBegin,tEnd][tBegin, tEnd]
loop<bool>Beim Erreichen der tEnd Marke wird auf tRewind zurück gesprungen und die Wiedergabe fortgesetzt. (def.: false)
tZoom<dbl>Zoomfaktor für die Zeitachse bei der Wiedergabe (def: 1.0),
>1.0>1.0: Zeitraffer
<1.0<1.0: Zeitlupe
endVar<str><bool>-Ausgabevariable, wird auf 'true' gesetzt, wenn der tEnd Marker erreicht ist.
tInfo<dbl>:=tRewind:= tRewind
tInfo[<dbl>, <dbl>]:=[tRewind,tEnd]:= [tRewind, tEnd]
tInfo[<dbl>, <dbl>, <dbl>]:=[tBegin,tRewind,tEnd]:= [tBegin, tRewind, tEnd]

Footnotes

  1. Mit Ausnahme der Kommentierung 2

  2. Ab Katalog Version 11 verfügbar.